#!/usr/bin/env python3
import lzma, sys, os, collections, itertools, math

def parse(path):
    opener = lzma.open if path.endswith('.xz') else open
    n=m=0; clauses=[]
    with opener(path,'rt',encoding='ascii',errors='ignore') as f:
        cur=[]
        for line in f:
            if not line or line[0] in 'c%0\n': continue
            if line[0]=='p':
                parts=line.split(); n=int(parts[2]); m=int(parts[3]); continue
            for tok in line.split():
                x=int(tok)
                if x==0:
                    if cur: clauses.append(cur); cur=[]
                else: cur.append(x)
        if cur: clauses.append(cur)
    return n,clauses

def onehots(n,clauses):
    neg=set()
    pos=[]
    for c in clauses:
        if len(c)==2 and c[0]<0 and c[1]<0:
            a,b=-c[0],-c[1]
            if a>b:a,b=b,a
            neg.add((a,b))
        elif len(c)>=2 and all(x>0 for x in c):
            pos.append(c)
    groups=[]; owned=[-1]*(n+1)
    for c in pos:
        ok=True
        for a,b in itertools.combinations(c,2):
            if a>b:a,b=b,a
            if (a,b) not in neg:
                ok=False; break
        if ok and all(owned[v]<0 for v in c):
            gid=len(groups); groups.append(c)
            for v in c: owned[v]=gid
    return groups, owned

def main(paths):
    for p in paths:
        n,cs=parse(p)
        cnt=collections.Counter(len(c) for c in cs)
        pc=sum(sum(1 for x in c if x>0) for c in cs)
        lits=sum(len(c) for c in cs)
        groups,owned=onehots(n,cs)
        glens=collections.Counter(len(g) for g in groups)
        deg=[0]*(n+1); pos=[0]*(n+1); neg=[0]*(n+1)
        for c in cs:
            for l in c:
                deg[abs(l)]+=1
                if l>0: pos[abs(l)]+=1
                else: neg[abs(l)]+=1
        used=sum(d>0 for d in deg)
        print('\n',os.path.basename(p))
        print('vars',n,'clauses',len(cs),'sizes',dict(sorted(cnt.items())),'posfrac',round(pc/lits,4),'used',used)
        print('onehots',len(groups),'lens',dict(sorted(glens.items())),'vars_in_groups',sum(len(g) for g in groups))
        print('first_groups',groups[:20])
        print('last_groups',groups[-10:])
        print('deg min/max/avg',min(d for d in deg[1:] if d),max(deg),round(sum(deg)/used,1))
        # contiguous runs of non-owned vars and owned vars
        runs=[]; cur=None; st=1
        for v in range(1,n+1):
            typ='G' if owned[v]>=0 else 'N'
            if cur is None: cur=typ; st=v
            elif typ!=cur:
                runs.append((cur,st,v-1)); cur=typ; st=v
        runs.append((cur,st,n))
        print('runs',runs[:30], '... total',len(runs))
        # positive all clauses not onehot lengths
        allpos=[c for c in cs if len(c)>=2 and all(x>0 for x in c)]
        print('allpos count',len(allpos),'first non-onehot?', next((c for c in allpos if not any(c is g for g in groups)), None))
        # unit clauses
        units=[c[0] for c in cs if len(c)==1]
        if units: print('units',len(units),units[:40])

if __name__=='__main__': main(sys.argv[1:])
