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


def read_dimacs(path):
    opener = lzma.open if path.endswith(".xz") else open
    nvars = 0
    clauses = []
    with opener(path, "rt") as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith("c"):
                continue
            if line.startswith("p"):
                parts = line.split()
                nvars = int(parts[2])
                continue
            lits = [int(x) for x in line.split() if x != "0"]
            if lits:
                clauses.append(tuple(lits))
    return nvars, clauses


def unit_propagate(clauses, initial=None):
    assign = {} if initial is None else dict(initial)
    queue = collections.deque()
    if initial is None:
        for c in clauses:
            if len(c) == 1:
                queue.append(c[0])

    def set_lit(lit):
        v = abs(lit)
        val = lit > 0
        old = assign.get(v)
        if old is not None:
            return old == val
        assign[v] = val
        return True

    for lit in list(queue):
        if not set_lit(lit):
            return assign, True, ("opposite-units", lit)

    while True:
        changed = False
        for c in clauses:
            sat = False
            unassigned = []
            for lit in c:
                val = assign.get(abs(lit))
                if val is None:
                    unassigned.append(lit)
                elif val == (lit > 0):
                    sat = True
                    break
            if sat:
                continue
            if not unassigned:
                return assign, True, c
            if len(unassigned) == 1:
                if not set_lit(unassigned[0]):
                    return assign, True, c
                changed = True
        if not changed:
            return assign, False, None


for path in sys.argv[1:]:
    nvars, clauses = read_dimacs(path)
    sizes = collections.Counter(map(len, clauses))
    pos = neg = 0
    deg = collections.Counter()
    unit_lits = []
    for c in clauses:
        for lit in c:
            deg[abs(lit)] += 1
            if lit > 0:
                pos += 1
            else:
                neg += 1
        if len(c) == 1:
            unit_lits.append(c[0])
    unit_assign = {}
    unit_conflict = False
    for lit in unit_lits:
        v = abs(lit)
        val = lit > 0
        if v in unit_assign and unit_assign[v] != val:
            unit_conflict = True
        unit_assign[v] = val
    rem = empty = 0
    rem_sizes = collections.Counter()
    for c in clauses:
        sat = False
        unresolved = 0
        for lit in c:
            val = unit_assign.get(abs(lit))
            if val is None:
                unresolved += 1
            elif val == (lit > 0):
                sat = True
                break
        if sat:
            continue
        if unresolved == 0:
            empty += 1
        else:
            rem += 1
            rem_sizes[unresolved] += 1
    propagated, prop_conflict, confl_clause = unit_propagate(clauses)
    degrees = [deg[i] for i in range(1, nvars + 1)]
    print(pathlib.Path(path).name)
    print("  nvars", nvars, "clauses", len(clauses), "sizes", dict(sorted(sizes.items())))
    print("  units", len(unit_lits), "unit_conflict", unit_conflict, "unit polarity", sum(l > 0 for l in unit_lits), sum(l < 0 for l in unit_lits))
    print("  after units remaining", rem, "empty", empty, "rem sizes", dict(sorted(rem_sizes.items())))
    print("  full UP assigned", len(propagated), "conflict", prop_conflict, "conflict_clause", confl_clause)
    print("  polarity pos/neg", pos, neg, "degree avg/min/max", round(statistics.mean(degrees), 2), min(degrees), max(degrees))
