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


def read_dimacs(path):
    opener = lzma.open if path.endswith(".xz") else open
    n = m = None
    clauses = []
    with opener(path, "rt") as f:
        for line in f:
            if not line or line[0] == "c":
                continue
            if line[0] == "p":
                parts = line.split()
                n = int(parts[2])
                m = int(parts[3])
                continue
            lits = [int(x) for x in line.split()]
            if lits and lits[-1] == 0:
                lits.pop()
            if lits:
                clauses.append(lits)
    return n, m, clauses


def sign_mask_for_tuple(clause, tup):
    pos = {v: i for i, v in enumerate(tup)}
    mask = 0
    for lit in clause:
        if lit > 0:
            mask |= 1 << pos[abs(lit)]
    return mask


def main(paths):
    for path in paths:
        n, m, clauses = read_dimacs(path)
        sizes = collections.Counter(map(len, clauses))
        tuples = collections.defaultdict(list)
        var_occ = [0] * (n + 1)
        signed_occ = collections.Counter()
        for c in clauses:
            tup = tuple(sorted(abs(x) for x in c))
            tuples[tup].append(c)
            for lit in c:
                var_occ[abs(lit)] += 1
                signed_occ[lit > 0] += 1
        mult = collections.Counter(len(v) for v in tuples.values())
        edge_deg = [0] * (n + 1)
        for tup in tuples:
            for v in tup:
                edge_deg[v] += 1
        table_shapes = collections.Counter()
        for tup, cs in tuples.items():
            masks = tuple(sorted(sign_mask_for_tuple(c, tup) for c in cs))
            table_shapes[masks] += 1
        top_shapes = table_shapes.most_common(5)
        print(os.path.basename(path))
        print(f"  n={n} m={m} clause_sizes={dict(sizes)} unique_tuples={len(tuples)}")
        print(f"  tuple_mult={dict(sorted(mult.items()))}")
        print(f"  edge_degree={dict(sorted(collections.Counter(edge_deg[1:]).items()))}")
        print(f"  literal_occ_minmax={min(var_occ[1:])}/{max(var_occ[1:])} pos_frac={signed_occ[True]/sum(signed_occ.values()):.3f}")
        print("  top_table_masks:")
        for shape, count in top_shapes:
            print(f"    {count}: {shape}")


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