#!/usr/bin/env python3
import sys,lzma,random,time,collections,math

def op(p): return lzma.open(p,'rt') if p.endswith('.xz') else open(p)
def solve(path,limit=60,seed=1):
 t0=time.time(); n=0; rows=[]; bins=[]
 with op(path) as f:
  for line in f:
   if not line or line[0]=='c': continue
   if line[0]=='p': n=int(line.split()[2]); continue
   arr=[int(x) for x in line.split() if x!='0']
   if len(arr)==2: bins.append(tuple(arr))
   elif arr: rows.append(arr)
 sel=[0]*(n+1); rowof=[-1]*(n+1); choiceof=[-1]*(n+1); choices=[]; rowchoices=[]
 for ri,r in enumerate(rows):
  rc=[]
  for lit in r:
   v=abs(lit); cid=len(choices); choices.append(v); rc.append(cid); sel[v]=lit; rowof[v]=ri; choiceof[v]=cid
  rowchoices.append(rc)
 adj=[[] for _ in choices]
 ext=0
 for a,b in bins:
  va,vb=abs(a),abs(b)
  if rowof[va]!=rowof[vb]:
   ca,cb=choiceof[va],choiceof[vb]; adj[ca].append(cb); adj[cb].append(ca); ext+=1
 rng=random.Random(seed); R=len(rows)
 assign=[rng.choice(rc) for rc in rowchoices]
 inv=[-1]*len(choices)
 for r,c in enumerate(assign): inv[c]=r
 # selected boolean per choice
 selected=[0]*len(choices)
 for c in assign: selected[c]=1
 rowconf=[0]*R; total=0
 for r,c in enumerate(assign):
  cnt=sum(1 for nb in adj[c] if selected[nb])
  rowconf[r]=cnt
 total=sum(rowconf)//2
 conflicted=[r for r in range(R) if rowconf[r]>0]; inconf=[False]*R
 for r in conflicted: inconf[r]=True
 steps=0; last=time.time(); best=total
 while time.time()-t0<limit:
  if total==0:
   print('SAT found steps',steps,'time',round(time.time()-t0,2),'parse+init?', 'R',R,'C',len(choices),'ext',ext, flush=True); return True
  if not conflicted: break
  # clean random picked conflicted
  for _ in range(10):
   idx=rng.randrange(len(conflicted)); r=conflicted[idx]
   if rowconf[r]>0: break
   inconf[r]=False; conflicted[idx]=conflicted[-1]; conflicted.pop()
  else: continue
  old=assign[r]
  bestcs=[]; bestval=10**9
  for c in rowchoices[r]:
   val=sum(1 for nb in adj[c] if selected[nb])
   if val<bestval: bestval=val; bestcs=[c]
   elif val==bestval: bestcs.append(c)
  # noise
  if rng.random()<0.03: new=rng.choice(rowchoices[r])
  else: new=rng.choice(bestcs)
  if new==old:
   steps+=1; continue
  affected=set([r])
  for nb in adj[old]:
   rr=inv[nb]
   if rr>=0: affected.add(rr)
  for nb in adj[new]:
   rr=inv[nb]
   if rr>=0: affected.add(rr)
  selected[old]=0; inv[old]=-1; selected[new]=1; inv[new]=r; assign[r]=new
  oldsum=sum(rowconf[x] for x in affected)
  for rr in affected:
   c=assign[rr]; rowconf[rr]=sum(1 for nb in adj[c] if selected[nb])
  newsum=sum(rowconf[x] for x in affected)
  total += (newsum-oldsum)//2
  for rr in affected:
   if rowconf[rr]>0 and not inconf[rr]: inconf[rr]=True; conflicted.append(rr)
  steps+=1
  if total<best:
   best=total; print('best',best,'steps',steps,'time',round(time.time()-t0,1), flush=True)
 print('NO time',round(time.time()-t0,2),'best',best,'total',total,'R',R,'C',len(choices),'ext',ext, flush=True); return False
if __name__=='__main__':
 solve(sys.argv[1], float(sys.argv[2]) if len(sys.argv)>2 else 60, int(sys.argv[3]) if len(sys.argv)>3 else 1)
