#!/usr/bin/env python
# -*- coding: utf-8 -*-

TIMEOUT=172800

import argparse
import sys
import re
import itertools
import os
import shutil
from glob import glob
from collections import defaultdict

EVCOUNT = re.compile('(?P<num>\d+)ev')
def evcount(line):
  m = EVCOUNT.search(line)
  if m: return int(m.group('num'))
  else: return None

TIME_REGEX=re.compile("^real\t(?P<min>\d*)m(?P<sec>\d*\.\d*)s$")
PARTIAL_WARNING="WARNING: Alloy timeout (only partial solutions)\n"
def parse_comparator_log(log):
  time, exhaustive = None, True
  with open(log) as f:
    for line in f:
      m = TIME_REGEX.match(line)
      if m:
        minutes = float(m.group("min"))
        seconds = float(m.group("sec"))
        time = (minutes * 60) + seconds
      if line == PARTIAL_WARNING:
        exhaustive = False
  return time, exhaustive

TEST_REGEX=re.compile("^(?P<arch>PPC|ARM8|X86) (?P<name>test_[0-9a-f]*)$")
WITNESS_REGEX=re.compile("^Positive: (?P<pos>[0-9]+), Negative: (?P<neg>[0-9]+)$")
def parse_result_log(log):
  tests, pos, neg = [], [], []
  niterations = []
  with open(log) as f:
    for line in f:
      m = TEST_REGEX.match(line)
      if m:
        tests.append(m.group("name"))
      m = WITNESS_REGEX.match(line)
      if m:
        pos.append(int(m.group("pos")))
        neg.append(int(m.group("neg")))
        niterations.append(pos[-1]+ neg[-1])
  assert len(tests) == len(pos) and len(tests) == len(neg)
  results = {}
  for t,p,n in itertools.izip(tests, pos, neg):
    assert t not in results
    results[t] = (p,n)
  return results

def all_results_have_same_tests(results):
  tests = results[0].keys().sort()
  for r in results[1:]:
    assert r.keys().sort() == tests

def merge(results):
  if results == []: return 0,0,0
  assert 0 < len(results)
  all_results_have_same_tests(results)
  seen, not_seen = [], []
  for t in results[0]:
    pos, neg = 0, 0
    for r in results:
      pos += r[t][0]
      neg += r[t][1]
    if 0 < pos: seen.append(t)
    elif 0 == pos: not_seen.append(t)
    else: assert False
  return len(results[0]), len(seen), len(not_seen)

def results_of_path(path):
  results = []
  for log in glob(os.path.join(path, "result_*.log")):
    results.append(parse_result_log(log))
  return merge(results)

def main(argv=None):
  parser = argparse.ArgumentParser(description="Make a table for a given result dir structure")
  parser.add_argument("-arch", help="Which arch directory to traverse", required=True)
  parser.add_argument("results_dir", help="Results dir")
  args = parser.parse_args(argv)

  timing = defaultdict(lambda:(0,True))
  for f in glob(os.path.join(args.results_dir, "[0-9]*ev_experiment.log")):
    numevents = evcount(f)
    time, exhaustive = parse_comparator_log(f)
    timing[numevents] = (time,exhaustive)

  results = {}
  results['Forbid'] = defaultdict(lambda:[0,0,0])
  for path in glob(os.path.join(args.results_dir, '*/')):
    numevents = evcount(path)
    if not numevents: continue
    forbid_path = os.path.join(path, "litmus", args.arch)
    results['Forbid'][numevents] = results_of_path(forbid_path)
  results['Allow'] = defaultdict(lambda:[0,0,0])
  for path in glob(os.path.join(args.results_dir, '*/')):
    numevents = evcount(path)
    if not numevents: continue
    allow_path = os.path.join(path, "allow", "litmus", args.arch)
    results['Allow'][numevents] = results_of_path(allow_path)

  def str_of_timing(time, is_exhaustive):
    if is_exhaustive: return "%.0f" % (time)
    else: return "$>$%d" % TIMEOUT

  tabcols = "rrrrrrrr"
  colwidth = 3
  cols = ["3-5", "6-8"]
  header = [ "\\phantom{00}T", "\\phantom{00}S", "\\phantom{0}$\\neg$S" ] *2
  total = {'Forbid':[0,0,0], 'Allow':[0,0,0]}
  def incr_totals(xs, ys):
    nonzeros = [ y for y in ys if 0 < y ]
    if nonzeros == []: return xs
    return [ x+y for (x,y) in zip(xs,ys) ]
  print """
\\begin{table}
\\begin{tabular}{%s}
\\toprule
\\textbf{$\left|E\\right|$} & \\textbf{Solve (Sec)} & \multicolumn{%s}{c}{\\textbf{Forbid}} & \multicolumn{%s}{c}{\\textbf{Allow}} \\\\
\\cmidrule(l){%s}\\cmidrule(l){%s}
& & %s \\\\
\midrule""" % (tabcols, colwidth, colwidth, cols[0], cols[1], " & ".join(header))
  nevents = set(results['Forbid'].keys() + results['Allow'].keys())
  for n in range(min(nevents), max(nevents)+1):
    if not timing[n][0]: continue
    line = [n]
    line.append(str_of_timing(*timing[n]))
    line.extend(results['Forbid'][n])
    line.extend(results['Allow'][n])
    line = [ str(x).rjust(4) for x in line ]
    print " & ".join(line) + "\\\\"

    for k in ['Forbid', 'Allow']:
      total[k] = incr_totals(total[k], results[k][n])
  print "\\midrule"
  print "\\textbf{Sum} & & " + " & ".join([ str(x).rjust(4) for x in total['Forbid']]) + " & " + " & ".join([ str(x).rjust(4) for x in total['Allow']]) + "\\\\"
  print """\\bottomrule
\\end{tabular}
\\end{table}"""

  return 0

if __name__ == '__main__':
  sys.exit(main())
