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


def open_text(path):
    if path.endswith(".xz"):
        return lzma.open(path, "rt", encoding="ascii", errors="ignore")
    return open(path, "rt", encoding="ascii", errors="ignore")


def read_clauses(path):
    with open_text(path) as f:
        for line in f:
            if not line or line[0] == "c":
                continue
            if line[0] == "p":
                parts = line.split()
                yield ("p", int(parts[2]), int(parts[3]))
                continue
            lits = [int(x) for x in line.split() if x != "0"]
            if lits:
                yield lits


def prefix_graph(path):
    it = read_clauses(path)
    tag, nvars, nclauses = next(it)
    edges = []
    maxv = 0
    idx = 0
    first_non = None
    for obj in it:
        idx += 1
        if len(obj) == 2 and obj[0] < 0 and obj[1] < 0:
            u, v = -obj[0] - 1, -obj[1] - 1
            edges.append((u, v))
            maxv = max(maxv, u + 1, v + 1)
        else:
            first_non = idx
            break
    return nvars, nclauses, maxv, edges, first_non


def shifted_bases_and_stats(path, edges, n):
    size = collections.Counter()
    signpat = collections.Counter()
    units = []
    max_var = 0
    bases = [0] if edges else []
    starts = []
    first = edges[0] if edges else None
    cand_base = None
    cand_pos = 0
    cand_start = None
    clause_idx = 0
    after_prefix = []
    for obj in read_clauses(path):
        if obj[0] == "p":
            continue
        clause_idx += 1
        c = obj
        size[len(c)] += 1
        neg = sum(1 for x in c if x < 0)
        signpat[(len(c), neg, len(c) - neg)] += 1
        for lit in c:
            max_var = max(max_var, abs(lit))
        if len(c) == 1:
            units.append(c[0])
        if len(after_prefix) < 80 and clause_idx > len(edges):
            after_prefix.append((clause_idx, c))

        if not edges:
            continue
        if clause_idx <= len(edges):
            continue

        if cand_base is not None:
            u, v = edges[cand_pos]
            if len(c) == 2 and c[0] == -(cand_base + u + 1) and c[1] == -(cand_base + v + 1):
                cand_pos += 1
                if cand_pos == len(edges):
                    bases.append(cand_base)
                    starts.append(cand_start)
                    cand_base = None
                    cand_pos = 0
                    cand_start = None
                continue
            cand_base = None
            cand_pos = 0
            cand_start = None

        u0, v0 = first
        if len(c) == 2 and c[0] < 0 and c[1] < 0:
            base = -c[0] - 1 - u0
            if base >= 0 and -c[1] - 1 == base + v0:
                cand_base = base
                cand_pos = 1
                cand_start = clause_idx
                if len(edges) == 1:
                    bases.append(base)
                    starts.append(cand_start)
                    cand_base = None
                    cand_pos = 0
                    cand_start = None
    return {
        "size": size,
        "signpat": signpat,
        "units": units,
        "bases": bases,
        "starts": starts,
        "max_var": max_var,
        "after_prefix": after_prefix,
    }


def main(paths):
    for path in paths:
        nvars, nclauses, n, edges, first_non = prefix_graph(path)
        st = shifted_bases_and_stats(path, edges, n)
        unit_set = {abs(x): x for x in st["units"]}
        source = [v for v in range(n) if unit_set.get(n + v + 1) == -(n + v + 1)]
        target_base = st["bases"][-1] if st["bases"] else None
        target = []
        if target_base is not None:
            for v in range(n):
                lit = unit_set.get(target_base + v + 1)
                if lit and lit > 0:
                    target.append(v)
        tdist = None
        if len(source) == len(target):
            tdist = len(set(source) - set(target))
        print(path)
        print("  p", nvars, nclauses, "graph n", n, "m", len(edges), "first_non", first_non)
        print("  bases", len(st["bases"]), "first", st["bases"][:4], "last", st["bases"][-4:])
        if len(st["bases"]) > 1:
            diffs = [b - a for a, b in zip(st["bases"], st["bases"][1:])]
            print("  base diffs", collections.Counter(diffs).most_common(8))
        print("  k", len(source), "target_k", len(target), "token_distance", tdist)
        print("  units", len(st["units"]), "pos", sum(x > 0 for x in st["units"]), "neg", sum(x < 0 for x in st["units"]))
        print("  sizes", sorted(st["size"].items()))
        print("  signpat", st["signpat"].most_common(12))
        print("  after prefix")
        for idx, c in st["after_prefix"][:20]:
            print("   ", idx, c)


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