### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ###
##                    308-522 -- Modelling and Simulation
##                                  Fall 2002
##                             --- ASSIGNMENT 1 ---
##
## Traversal.py
##
## last modified: 09/25/02
### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ###

# "blockList" is used to store blocks with a nonempty "block_name" attribute
# that are met while traversing the graph from an integrator block. This list is
# used by "EXPORT_LaTeX.exportLaTeX" (i.e., the list is filled in the function
# below only when "flag == 0").
blockList = []

# "lookUp" is a dictionnary that maps each integrator name to a unique index.
# This disctioonary is used by "EXPORT_Mfile.exportMfile" (i.e., used only
# when "flag == 1").
lookUp = {}

def traverse(node, flag):
  """ Traverse the graph backward from the connector "node". This is a recursive
  function: "flag" specifies when the recursion bottoms up:
        1 -- when an Integrator or Constant block is met
             used by "EXPORT_Mfile.py"
        0 -- same as above, or when a block with a nonempty "block_name"
             attribute is met
             used by "EXPORT_LaTeX.py"
  The function returns a string summarizing the traversal.
  """

  # Find block upstream:
  block = node.in_connections_[0]  # only one block as a source

  # Depending on "flag", stop here is the block has a name. The block is
  # appended to "blockList" to be further processed in
  # "EXPORT_LaTeX.exportLaTeX". Note that Integrator blocks are not appended
  # to the list, since they are processed differently in the latter function.
  if not flag and block.block_name.getValue() != "":
    if block.block_type.getValue()[1] != 6:  # exclude integrators
      blockList.append( block )
    return block.block_name.getValue()  # bottom up

  # Bottom up when "flag == 1":
  # Note that we don't need to check the block is a constant: termination is
  # implicit in that case, since Constant blocks have no input.
  if block.block_type.getValue()[1] == 6:
    return stringRepres(block, [], flag)

  block.visited = 1  # used only when "flag == 0"

  # List of nodes connecting to the block:
  inNodes = block.in_connections_

  # Recursive call (traverse the graph):
  stringList = []
  for x in inNodes:
    stringList.append( traverse(x, flag) )

  # Build and return the string:
  return stringRepres(block, stringList, flag)


def stringRepres(block, strList, flag):
  """ Return the string representation of the function in "block", parameterized
  with the strings in "strList".
  "flag: signals whether the string is to be used for a LaTeX file (0), or for
  an m-file (1)
  """

  blockType = block.block_type.getValue()[1]

  if blockType == 0:  # Generic Block
    return block.block_operator.getValue() + "(" + strList[0]  + ")"

  elif blockType == 1:  # Negator Block
    return "-(" + strList[0] + ") "

  elif blockType == 2:  # Inverter Block
    if flag:
      return " (1./" + strList[0] + ") "
    else:
      return " \\frac{1}{" + strList[0] + "} "

  elif blockType == 3:  # Adder Block
    S = " ("
    for x in strList:
      S += x + "+"
    S = S[:-1] + ") "
    return S

  elif blockType == 4:  # Product Block
    # note: don't need parenthesis because product has highest precedence.
    S = ""
    for x in strList:
      if flag:
        S += x + "*"
      else:
        S += x + "\\cdot "
    if flag:
      S = S[:-1]
    else:
      S = S[:-6]
    return S

  elif blockType == 5:  # Delay Block
    raise NotImplementedError, "delay block?!"

  elif blockType == 6:  # Integrator Block
    if flag:
      return "x(" + str( lookUp[ block.block_name.getValue() ] ) + ")"
    else:
      S = block.block_name.getValue()
      if S == "":
        raise SyntaxError, "missing integrator name"
      return S

  elif blockType == 7:  # Derivative Block
    raise NotImplementedError, "derivative block?!"

  elif blockType == 8:  # Constant Block
    # "strList" should be empty
    return str( block.block_out_value.getValue() )

  elif blockType == 9:  # Time Block
   # REVIEW
    # "strList" should be empty
    S = block.block_name.getValue()
    if S == "":
      raise SyntaxError, "missing clock name"
    return S

