#!/usr/bin/env python3
import collections
import lzma
import sys
from pathlib import Path


def open_text(path: Path):
    if path.suffix == ".xz":
        return lzma.open(path, "rt")
    return path.open("rt")


def parse(path: Path):
    comments = []
    nvars = nclauses = None
    clauses = []
    with open_text(path) as f:
        cur = []
        for line in f:
            line = line.strip()
            if not line:
                continue
            if line.startswith("c"):
                comments.append(line)
                continue
            if line.startswith("p"):
                parts = line.split()
                nvars = int(parts[2])
                nclauses = int(parts[3])
                continue
            for tok in line.split():
                lit = int(tok)
                if lit == 0:
                    if cur:
                        clauses.append(tuple(cur))
                        cur = []
                    continue
                cur.append(lit)
    return comments, nvars, nclauses, clauses


def runs(vals):
    vals = sorted(set(vals))
    out = []
    if not vals:
        return out
    s = p = vals[0]
    for v in vals[1:]:
        if v == p + 1:
            p = v
        else:
            out.append((s, p, p - s + 1))
            s = p = v
    out.append((s, p, p - s + 1))
    return out


def main():
    for arg in sys.argv[1:]:
        path = Path(arg)
        comments, nvars, nclauses, clauses = parse(path)
        sizes = collections.Counter(len(c) for c in clauses)
        pos = [0] * (nvars + 1)
        neg = [0] * (nvars + 1)
        first_occ = [10**18] * (nvars + 1)
        last_occ = [-1] * (nvars + 1)
        for i, c in enumerate(clauses):
            for lit in c:
                v = abs(lit)
                if lit > 0:
                    pos[v] += 1
                else:
                    neg[v] += 1
                first_occ[v] = min(first_occ[v], i)
                last_occ[v] = max(last_occ[v], i)
        pure_pos = [v for v in range(1, nvars + 1) if pos[v] and not neg[v]]
        pure_neg = [v for v in range(1, nvars + 1) if neg[v] and not pos[v]]
        unused = [v for v in range(1, nvars + 1) if not pos[v] and not neg[v]]
        print("==", path.name)
        for c in comments[:10]:
            print(c)
        print("vars", nvars, "clauses", len(clauses), "decl", nclauses)
        print("sizes", dict(sorted(sizes.items())))
        print("pure_pos", len(pure_pos), runs(pure_pos)[:12])
        print("pure_neg", len(pure_neg), runs(pure_neg)[:12])
        print("unused", len(unused), runs(unused)[:12])
        print("first 20 clauses:")
        for c in clauses[:20]:
            print(" ", c)
        print("last 20 clauses:")
        for c in clauses[-20:]:
            print(" ", c)
        # Variables whose first occurrence is early often mark object block boundaries.
        interesting = []
        for v in range(1, min(nvars, 1000) + 1):
            interesting.append((first_occ[v], v, pos[v], neg[v], last_occ[v]))
        interesting.sort()
        print("early-var-order", interesting[:80])
        print()


if __name__ == "__main__":
    main()
