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


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


def infer_m(gate_count):
    m = 1
    while math.comb(m, 3) < gate_count:
        m += 1
    return m if math.comb(m, 3) == gate_count else None


def gate3(clauses, i):
    c1, c2, c3 = clauses[i], clauses[i + 1], clauses[i + 2]
    if len(c1) == len(c2) == len(c3) == 3:
        a, b, c = -c1[0], -c1[1], c1[2]
        if a > 0 and b > 0 and c > 0 and c2 == (-a, b, -c) and c3 == (a, -b, -c):
            return a, b, c
    return None


def infer_pair_map(clauses):
    gate_count = 0
    while gate_count * 3 + 2 < len(clauses) and gate3(clauses, gate_count * 3):
        gate_count += 1
    m = infer_m(gate_count)
    if m is None:
        raise SystemExit("not a comb(m,3) gate block")
    # The first m-1 variables y[0,j] are the 'a' value 1 and the b values in
    # the first gate run.
    y = [[None] * m for _ in range(m)]
    y[0][1] = y[1][0] = 1
    for off in range(m - 2):
        _, b, c = gate3(clauses, off * 3)
        j = off + 2
        y[0][j] = y[j][0] = b
        y[1][j] = y[j][1] = c
    # Every transitivity gate whose first two vars are y[0,i], y[0,j] gives y[i,j].
    pos_to_pair = {}
    for i in range(m):
        for j in range(i + 1, m):
            if y[i][j] is not None:
                pos_to_pair[y[i][j]] = (i, j)
    for gi in range(gate_count):
        a, b, c = gate3(clauses, gi * 3)
        ai = bj = None
        for t in range(1, m):
            if y[0][t] == a:
                ai = t
            if y[0][t] == b:
                bj = t
        if ai is not None and bj is not None and ai < bj and y[ai][bj] is None:
            y[ai][bj] = y[bj][ai] = c
            pos_to_pair[c] = (ai, bj)
    return m, gate_count, pos_to_pair, y


def main(argv):
    n, clauses = read_clauses(argv[1])
    m, gates, var_to_pair, y = infer_pair_map(clauses)
    print("M", m, "pair-vars", math.comb(m, 2), "gates", gates)
    for v in sorted(var_to_pair)[:80]:
        print(v, var_to_pair[v])
    print("...")
    for v in sorted(var_to_pair)[-80:]:
        print(v, var_to_pair[v])
    if len(argv) > 2:
        for s in argv[2:]:
            v = int(s)
            print("lookup", v, var_to_pair.get(abs(v)))


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