#! /usr/bin/python

#
# slot.py
# assign COT cables to slots and repeaters
# begun 1999-11-18 by WJA
#

import string, sys, os
from math import *

minslack = 30
cellgroup = 8  # number of cells to route as a group
ncl_sl = map(lambda x: 4*x, [30, 42, 48, 60, 72, 84, 96, 108, 120])
celllist_sl = {}
r_sl = [-1, 46.774, 58.534, 70.295, 82.055, 93.815, 105.575, 117.335, 129.096]
color_sl = [0, 2, 2, 3, 3, 4, 4, 6, 6]
sl_east = [1, 4, 5, 8]; sl_west = [2, 3, 6, 7]; sl_ew = [sl_east, sl_west]
iew_sl = [-1, 0, 1, 1, 0, 0, 1, 1, 0]
r_inslot = 143.0; r_outslot = 86*2.54; l_slot = 7.5*12*2.54
r_outslotdraw = 165.0
r_repeater = 100*2.54
l_cable = map(lambda x: (x-0.5)*12*2.54,
              [-1, 14, 14, 13, 13, 13, 13, 12.5, 12])
x_crate = map(lambda x: 315*x, [1, 1, 1, -1, -1, -1, -1, -1, 1, 1])
y_crate = map(lambda x: 110*x, [1, 2, 3, 2, 1, 0, -2, -3, -2, -1])
dy_slot = map(lambda x: 4.4*x, [-1, -1, -1, 1, 1, 1, 1, 1, -1, -1])
num_repeaters = 760
phi_repeater = {}
num_slot = [ \
     1,   2,   3,   5,    8,  11,  15,  16,  19,
     22,  26,  29,  30,  33,  35,  40,  43,
     44,  47,  50,  54,  57,  58,  61,  64,
     68,  70,  71,  72,  73,  74,  75,  77,  82,  84,
     87,  88,  91,  96,  98, 101,
     102, 105, 110, 112, 115, 116, 120, 125,
     129, 130, 133, 135, 140, 142, 143, 144
     ]
phi_slot = {}
use_slot = [ \
  0, 0, 0, 0, 0, 0, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 0,
  0, 1, 0, 0, 0, 0, 0, 0 ]
slot_east = {}; slot_west = {}; slot_ew = [slot_east, slot_west]
rep_east = {}; rep_west = {}; rep_ew = [rep_east, rep_west]
tdcmap = {}
celldata = {}

class Slot:

  def __init__(self, num, phi):
    self.num = num
    self.phi = phi
    # number of cells routed through this slot
    self.ncells = 0
    # free space (in cells) available in this slot
    self.free = 24

  def draw(self):
    p = self.phi
    r1 = r_inslot
    x1 = r1*cos(p)
    y1 = r1*sin(p)
    r2 = r_outslotdraw
    x2 = r2*cos(p)
    y2 = r2*sin(p)
    if self.free>=cellgroup:
      print "set plci 1"
    else:
      print "set plci 4"
    print "line %.1f %.1f %.1f %.1f"%(x1, y1, x2, y2)

class Repeater:

  def __init__(self, iew, num, phi):
    self.num = num
    self.phi = phi
    self.cell = None
    self.iew = iew

  def next(self):
    skip = cellgroup/2
    j = (self.num+skip)%(skip*len(rep_ew[self.iew].keys()))
    if j==0: j = skip*len(rep_ew[self.iew].keys())
    return rep_ew[self.iew][j]

  def prev(self):
    skip = cellgroup/2
    j = (self.num-skip)%(skip*len(rep_ew[self.iew].keys()))
    if j==0: j = skip*len(rep_ew[self.iew].keys())
    return rep_ew[self.iew][j]

  def draw(self):
    p = self.phi
    r1 = r_repeater-2
    x1 = r1*cos(p)
    y1 = r1*sin(p)
    r2 = r_repeater+2
    x2 = r2*cos(p)
    y2 = r2*sin(p)
    if self.cell==None:
      print "set plci 1"
    else:
      sl = self.cell[0]
      print "set plci %d"%(color_sl[sl])
    print "line %.1f %.1f %.1f %.1f"%(x1, y1, x2, y2)

class Cell:

  def __init__(self, sl, cell):
    self.sl = sl
    self.cell = cell
    self.phidumb = 2*pi*cell/ncl_sl[self.sl]
    stereo_offset = 3*(sl%2)  # number of cells shifted for stereo angle
    eff_cellnum = cell + (cellgroup-1)/2.0 + stereo_offset
    self.phieff = 2*pi*eff_cellnum/ncl_sl[self.sl]
    self.phieff = self.phieff%(2*pi)
    self.phi = self.phieff
    self.iew = iew_sl[self.sl]
    self.slotnum = None
    self.repnum = None

  def draw_cell(self):
    r = r_sl[self.sl]
    dr = 2.0
    p = self.phi
    print "line %.1f %.1f %.1f %.1f"%( \
      (r-dr)*cos(p), (r-dr)*sin(p), (r+dr)*cos(p), (r+dr)*sin(p))

  def draw_celltoslot(self):
    rc = r_sl[self.sl]
    pc = self.phi
    rs = r_inslot
    ps = self.slot().phi
    print "line %.1f %.1f %.1f %.1f"%( \
      rc*cos(pc), rc*sin(pc), rs*cos(ps), rs*sin(ps))

  def draw_slottorep(self):
    rs = r_outslotdraw
    ps = self.slot().phi
    rr = r_repeater
    pr = self.rep().phi
    print "line %.1f %.1f %.1f %.1f"%( \
      rs*cos(ps), rs*sin(ps), rr*cos(pr), rr*sin(pr))

  def draw_reptotdc(self):
    rr = r_repeater
    pr = self.rep().phi
    print "line %.1f %.1f %.1f %.1f"%( \
      rr*cos(pr), rr*sin(pr), self.xtdc(), self.ytdc())

  def draw(self):
    self.draw_cell()
    self.draw_celltoslot()
    self.draw_slottorep()
    self.draw_reptotdc()

  def y_outslot(self):
    return r_outslot*sin(self.slot().phi)

  def slot(self):
    # get slot object corresponding to this cell's slot number
    return slot_ew[self.iew][self.slotnum]

  def rep(self):
    # get repeater object corresponding to this cell's repeater number
    return rep_ew[self.iew][self.repnum]

  def yrep(self):
    return r_repeater*sin(self.rep().phi)

  def tdcmap(self):
    if not tdcmap.has_key((self.sl, self.cell)):
      return None
    else:
      return tdcmap[(self.sl, self.cell)]

  def xtdc(self):
    crate, slot, conn = self.tdcmap()
    return x_crate[crate%10]

  def ytdc(self):
    crate, slot, conn = self.tdcmap()
    return y_crate[crate%10]+dy_slot[crate%10]*slot

  def lcable(self):
    return l_cable[self.sl]

  def slack_norep(self):
    # compute slack if we stop at outside of slot--no repeater
    rslot = r_inslot
    rcell = r_sl[self.sl]
    dp = dphi(self.slot().phi, self.phi)
    # distance from cell to inside of slot
    dist = sqrt(pow(rslot,2)+pow(rcell,2)-2*rslot*rcell*cos(dp))
    # add length of slot
    dist = dist+l_slot
    # subtract from cable length
    return l_cable[self.sl]-dist

  def testrep_slack(self, repeater):
    # compute slack including repeater assignment
    rs = r_outslot; rr = r_repeater
    dp = dphi(repeater.phi, self.slot().phi)
    # distance from slot exit to repeater
    dist = sqrt(pow(rs,2)+pow(rr,2)-2*rs*rr*cos(dp))
    # subtract this from slack before repeater
    return self.slack_norep()-dist

  def slack(self):
    # compute slack including repeater assignment
    return self.testrep_slack(self.rep())

def init():
  slotnum = 0
  for islot in range(len(use_slot)):
    if not use_slot[islot]: continue
    slotnum = slotnum+1
    phi = (islot+0.5)*2*pi/192
    phi_slot[slotnum] = phi
  for l in open("tdcmap.dat").readlines():
    x = map(string.atoi, string.split(l))
    sl = x[0]
    cell = x[1]
    tdccrate = x[2]
    tdcslot = x[3]
    tdcconn = x[4]
    tdcmap[(sl, cell)] = (tdccrate, tdcslot, tdcconn)
  if not (cellgroup in [2, 4, 8]):
    raise ValueError, "cellgroup must be 2, 4, or 8"
  for i in range(len(ncl_sl)):
    celllist_sl[i] = []
    for j in range(ncl_sl[i]):
      if not tdcmap.has_key((i, j)): continue
      tdcconn = tdcmap[(i, j)][2]
      if tdcconn%(cellgroup/2)!=0: continue
      celllist_sl[i].append(j)
  for i in range(len(num_slot)):
    n = num_slot[i]
    p = phi_slot[n]
    slot_east[n] = Slot(n, p)
    slot_west[n] = Slot(n, p)
  for i in range(1, 1+num_repeaters):
    space = 55.0
    nnn = num_repeaters+4*space
    if i<=38:
      p = 2*pi*(i+0*space)/nnn
    elif i<=342:
      p = 2*pi*(i+1*space)/nnn
    elif i<=418:
      p = 2*pi*(i+2*space)/nnn
    elif i<=722:
      p = 2*pi*(i+3*space)/nnn
    else:
      p = 2*pi*(i+4*space)/nnn
    p = p - 2*pi/nnn/2
    phi_repeater[i] = fmod(p+4*pi, 2*pi)
    if i%(cellgroup/2)!=0:
      continue
    rep_east[i] = Repeater(0, i, p)
    rep_west[i] = Repeater(1, i, p)
  lastphi = 0
  if 0:
    for i in range(1, 1+num_repeaters):
      dp = phi_repeater[i]-lastphi
      print "repeater %d  phi %.1f  dp %.2f"%(
        i, 180.0*phi_repeater[i]/pi, 180.0*dp/pi)
      lastphi = phi_repeater[i]
    sys.exit(-1)
  for sl in sl_east+sl_west:
    for cell in celllist_sl[sl]:
      celldata[(sl, cell)] = Cell(sl, cell)

def usedrep(iew):
  reps = {}
  for sl in sl_ew[iew]:
    for cell in celllist_sl[sl]:
      r = celldata[(sl, cell)].repnum
      if reps.has_key(r):
        raise InternalError, "duplicated repeater!"
      reps[r] = 0
  nrep = len(reps.keys())
  return nrep*cellgroup/2

def dphi(p1, p2):
  dp = p2-p1
  while dp<-pi:
    dp = dp+2*pi
  while dp>pi:
    dp = dp-2*pi
  return dp 

def dump_slots():
  for iew in range(2):
    for slot in slot_ew[iew].values():
      print "ew=%d slot=%d ncells=%d"%(iew, slot.num, slot.ncells)

def assign_slots():
  print "assigning slots"
  for iew in range(2):
    # loop through superlayers for this side of the chamber (E/W)
    for sl in sl_ew[iew]:
      print "  sl", sl
      # hack to order cells by abs(ycell)
      list = []
      for cell in celllist_sl[sl]:
        p = celldata[(sl, cell)].phi
        list.append((abs(sin(p)), cell))
      list.sort()  
      # loop through cells in order
      for x in list:
        cell = x[1]
        p = celldata[(sl, cell)].phi
        # pick non-full slot that is closest in phi
        best = None
        dpbest = 999
        for slot in slot_ew[iew].values():
          if slot.free>=cellgroup:
            slotnum = slot.num
            tdccrate = celldata[(sl, cell)].tdcmap()[0]
            slotlist = [1, 2, 143, 144, 71, 72, 73, 74]
            if slotnum in slotlist and tdccrate%5!=0:
              continue
            if tdccrate%10==4 and sin(slot.phi)<0:
              continue
            dp = abs(dphi(p, slot.phi))
            if dpminslack+(r_repeater-r_outslot)*2
        sort = (okslack, abs(y-ycut), slack)
        info = (sl, cell)
        celllist.append((sort, info))
    celllist.sort()
    celllist = map(lambda x: x[1], celllist)
    # process cells in order
    for x in celllist:
      sl, cell = x
      cd = celldata[(sl, cell)]
      y = abs(cd.y_outslot())
      slotnum = cd.slotnum
      p = cd.slot().phi
      if cd.tdcmap()==None:
        print "no TDC mapping for sl=%d cell=%d"%(sl, cell)
      xtdc, ytdc = cd.xtdc(), cd.ytdc()
      # find unused repeater closest in phi, with constraint that
      # repeaters near phi=0,pi are used only for slots near 0,pi
      best = None
      dpbest = 999
      for rep in rep_ew[iew].values():
        if rep.cell!=None: continue
        # hack to decide ("slot near 0,pi" xor "repeater near 0,pi")
        if sin(p)<0:
          ycut1 = ycut_lower
        else:
          ycut1 = ycut_upper
        yrep = r_repeater*abs(sin(rep.phi))
        isl = cd.sl
        crate = cd.tdcmap()[0]
        #if (y>ycut1)!=(yrep>ycut1): continue
        xrep = r_repeater*cos(rep.phi)
        # use only north (south) repeater for north (south) tdc
        if (xtdc>0)!=(xrep>0): continue 
        # look for best match in phi
        dp = abs(dphi(p, rep.phi))
        if dpcd1.slack():
            # we can gain slack by moving to adjacent repeater
            swappedany = 1
            rep.cell = cd1.rep().cell
            cd1.rep().cell = None
            cd1.repnum = rep.num
        # then loop through other cells with equal or greater ytdc
        for j in range(i+1, len(celllist)):
          sl2, cell2 = celllist[j]
          cd2 = celldata[(sl2, cell2)]
          xt2, yt2 = cd2.xtdc(), cd2.ytdc()
          # only swap among same-side (N/S) tdcs
          if (xt1>0) != (xt2>0): continue
          yr1, yr2 = cd1.yrep(), cd2.yrep()
          r1, r2 = cd1.rep(), cd2.rep()
          # if this swap would dig us out of a hole, take it
          badbefore = cd1.slack()yr2, we're out of order
          if yr1>yr2 or fixed:
            if cd1.testrep_slack(r2)