def decomp_4rdm_to_3rdm_sf(d4, d1, d2, d2_hom, d2_het, d3):
  """
  Returns the list of terms composing the decomposition of the 4 body spin free rdm d4
  in terms of one, two, and three body rdms"
  """

  # This decomposition assumes the density matrices correspond to a spin averaged ensemble state.

  # d4 -- Spin free 4rdm to be decomposed
  # d1 -- One body spin free rdm
  # d2 -- Two body spin free rdm
  # d2_hom -- Sum of all alpha and all beta two body rdms.  The "homogeneous spin" 2rdm.
  # d2_het -- Sum of abab and baba two body rdms.  The "heterogeneous spin" 2rdm.
  # d3 -- Three body spin free rdm

  # Note that d2 = d2_hom + d2_het

  # Check input
  if ( not isinstance(d4, tensor) ) or (len(d4.indices) != 8):
    raise TypeError, "d4 must be a tensor with 8 indices"
  if ( not isinstance(d1, tensor) ) or (len(d1.indices) != 2):
    raise TypeError, "d1 must be a tensor with 2 indices"
  if ( not isinstance(d2, tensor) ) or (len(d2.indices) != 4):
    raise TypeError, "d2 must be a tensor with 4 indices"
  if ( not isinstance(d2_hom, tensor) ) or (len(d2_hom.indices) != 4):
    raise TypeError, "d2_hom must be a tensor with 4 indices"
  if ( not isinstance(d2_het, tensor) ) or (len(d2_het.indices) != 4):
    raise TypeError, "d2_het must be a tensor with 4 indices"
  if ( not isinstance(d3, tensor) ) or (len(d3.indices) != 6):
    raise TypeError, "d3 must be a tensor with 6 indices"

  # Initialize the return value
  decomp = []

  # Compute terms from the sum involving the 3-body cumulant
  for p in range(4):
    ti = range(4)
    del(ti[p])
    ti.insert(0,p)
    for q in range(4):
      bi = range(4)
      del(bi[q])
      bi.insert(0,q)
      for i in range(1,4):
        for j in range(1,4):
          if i != j and ti[i] == bi[j]:
            temp = bi[i]
            bi[i] = bi[j]
            bi[j] = temp

      # Determine the number of index permutations
      n_perm = get_num_perms(ti,bi)

      # Determine the sign
      sign = (-1)**n_perm

      # Create the 1RDM 3RDM term
      bj = [i+4 for i in bi]
      coeff = sign * (0.5)**n_perm
      d1_copy_0 = d1.copy()
      d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
      d3_copy_0 = d3.copy()
      d3_copy_0.indices = [d4.indices[i] for i in (ti[1:4] + bj[1:4])]
      decomp.append(term(coeff, [], [d1_copy_0, d3_copy_0]))

      # Create terms not involving the 3RDM
      d3_decomp = decomp_3rdm_to_2rdm_sf(d3_copy_0, d1, d2)
      for t in d3_decomp:
        coeff = (-1) * sign * (0.5)**n_perm * t.numConstant
        decomp.append(term(coeff, [], [d1_copy_0] + t.tensors))
      
  # Compute terms from the sum involving two 2-body cumulants
  for p in range(1):
    for q in range(p+1,4):
      ti = [p,q]
      for i in range(4):
        if not (i in ti):
          ti.append(i)
      for r in range(4):
        for s in range(r+1,4):
          if s == p or r == q:
            bi = [s,r]
          else:
            bi = [r,s]
          temp = range(4)
          del temp[s]
          del temp[r]
          if temp[0] == ti[3] or temp[1] == ti[2]:
            temp = [temp[1], temp[0]]
          bi += temp
          
          # Determine the number of index permutations
          n_perm = get_num_perms(ti,bi)

          # Determine the sign
          sign = (-1)**n_perm

          # Create 2RDM 2RDM term
          bj = [i+4 for i in bi]
          if n_perm < 2:
            coeff = sign * (0.5)**n_perm
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d2_copy_1 = d2.copy()
            d2_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            decomp.append(term(coeff, [], [d2_copy_0, d2_copy_1]))
          elif n_perm == 2:
            coeff = sign * 0.5
            d2_hom_copy_0 = d2_hom.copy()
            d2_hom_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d2_hom_copy_1 = d2_hom.copy()
            d2_hom_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            decomp.append(term(coeff, [], [d2_hom_copy_0, d2_hom_copy_1]))
            d2_het_copy_0 = d2_het.copy()
            d2_het_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d2_het_copy_1 = d2_het.copy()
            d2_het_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            decomp.append(term(coeff, [], [d2_het_copy_0, d2_het_copy_1]))
            d2_het_copy_0 = d2_het.copy()
            d2_het_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + [bj[1], bj[0]])]
            d2_het_copy_1 = d2_het.copy()
            d2_het_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + [bj[3], bj[2]])]
            decomp.append(term(coeff, [], [d2_het_copy_0, d2_het_copy_1]))
          else:
            raise RuntimeError, "Did not expect a term with %i permutations" %n_perm

          # Create 'un-permuted' 2RDM 1RDM 1RDM terms
          coeff = sign * (-1) * (0.5)**n_perm 
          d2_copy_0 = d2.copy()
          d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
          d1_copy_0 = d1.copy()
          d1_copy_0.indices = [d4.indices[i] for i in (ti[2:3] + bj[2:3])]
          d1_copy_1 = d1.copy()
          d1_copy_1.indices = [d4.indices[i] for i in (ti[3:4] + bj[3:4])]
          decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
          d2_copy_0 = d2.copy()
          d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
          d1_copy_0 = d1.copy()
          d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
          d1_copy_1 = d1.copy()
          d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])]
          decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))

          # Create 'permuted' 2RDM 1RDM 1RDM terms
          if n_perm < 2:
            bj = bi[0:2] + bi[3:4] + bi[2:3]
            n_perm = get_num_perms(ti,bj)
            bj = [i+4 for i in bj]
            coeff = sign * (0.5)**n_perm
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d1_copy_0 = d1.copy()
            d1_copy_0.indices = [d4.indices[i] for i in (ti[2:3] + bj[2:3])]
            d1_copy_1 = d1.copy()
            d1_copy_1.indices = [d4.indices[i] for i in (ti[3:4] + bj[3:4])]
            decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
            bj = bi[1:2] + bi[0:1] + bi[2:4]
            n_perm = get_num_perms(ti,bj)
            bj = [i+4 for i in bj]
            coeff = sign * (0.5)**n_perm
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            d1_copy_0 = d1.copy()
            d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
            d1_copy_1 = d1.copy()
            d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])]
            decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
          elif n_perm == 2:
            bj = bi[1:2] + bi[0:1] + bi[3:4] + bi[2:3]
            n_perm = get_num_perms(ti,bj)
            bj = [i+4 for i in bj]
            coeff = sign * (-1) * (0.5)**n_perm
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d1_copy_0 = d1.copy()
            d1_copy_0.indices = [d4.indices[i] for i in (ti[2:3] + bj[2:3])]
            d1_copy_1 = d1.copy()
            d1_copy_1.indices = [d4.indices[i] for i in (ti[3:4] + bj[3:4])]
            decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            d1_copy_0 = d1.copy()
            d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
            d1_copy_1 = d1.copy()
            d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])]
            decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
          else:
            raise RuntimeError, "Did not expect a term with %i permutations" %n_perm

          # Create 1RDM 1RDM 1RDM 1RDM terms
          bj = [i for i in bi]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))
          bj = bi[0:2] + bi[3:4] + bi[2:3]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (-1) * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))
          bj = bi[1:2] + bi[0:1] + bi[2:4]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (-1) * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))
          bj = bi[1:2] + bi[0:1] + bi[3:4] + bi[2:3]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))

  # Compute terms from the sum involving one 2-body cumulant
  for p in range(4):
    for q in range(p+1,4):
      ti = [p,q]
      for i in range(4):
        if not (i in ti):
          ti.append(i)
      for r in range(4):
        for s in range(4):
          if r == s:
            continue
          bi = [r,s]
          temp = range(4)
          del temp[max(r,s)]
          del temp[min(r,s)]
          if temp[0] == ti[3] or temp[1] == ti[2]:
            temp = [temp[1], temp[0]]
          bi += temp

          # Determine the number of index permutations
          n_perm = get_num_perms(ti,bi)

          # Determine the sign
          sign = (-1)**n_perm

          # Compute 1RDM 1RDM 2RDM term
          coeff = 1
          bj = [i for i in bi]
          pairs_rdm1 = [ [ti[i],bj[i]] for i in range(2)]
          pairs_rdm2 = [ [bj[i],ti[i]] for i in range(2,4)]
          for pair in pairs_rdm2:
            if pair[0] == pair[1]:
              pairs_rdm2 = []
              break
          for pair in pairs_rdm2:
            if not (pair in pairs_rdm1):
              #print "swapping bj[2] and bj[3]"
              (bj[3],bj[2]) = (bj[2],bj[3])
              coeff *= -1
              break
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff *= sign * (0.5)**n_perm
          d1_copy_0 = d1.copy()
          d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
          d1_copy_1 = d1.copy()
          d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])]
          d2_copy_0 = d2.copy()
          d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
          decomp.append(term(coeff, [], [d1_copy_0, d1_copy_1, d2_copy_0]))

          # Compute 'un-permuted' 1RDM 1RDM 1RDM 1RDM term
          bj = [i for i in bi]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (-1) * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))

          # Compute 'permuted' 1RDM 1RDM 1RDM 1RDM term
          bj = bi[0:2] + bi[3:4] + bi[2:3]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))

  # Compute terms from the sum involving only one particle density matrices
  ti = range(4)
  for bi in makePermutations(4):

    # Determine the number of permutations
    n_perm = get_num_perms(ti,bi)

    # Determine the sign
    sign = (-1)**n_perm

    # Compute 1RDM 1RDM 1RDM 1RDM term
    bj = [i+4 for i in bi]
    coeff = sign * (0.5)**n_perm
    d1_copies = []
    for j in range(4):
      d1_copies.append(d1.copy())
      d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
    decomp.append(term(coeff, [], d1_copies))

  # Combine like terms
  for t in decomp:
    t.tensors.sort()
  decomp.sort()
  i = 0
  while i < len(decomp)-1:
    if decomp[i].tensors == decomp[i+1].tensors:
      decomp[i+1].numConstant += decomp[i].numConstant
      del(decomp[i])
    else:
      i += 1
  termChop(decomp)

  # Return the decomposition
  return decomp
def decomp_3rdm_to_2rdm_sf(d3, d1, d2, indexOrder = '1212'):
  """
  Returns the list of terms composing the decomposition of the 3 body spin free rdm d3
  in terms of one and two body rdms"
  """

  if ( not isinstance(d1, tensor) ) or (len(d1.indices) != 2):
    raise TypeError, "d1 must be a tensor with 2 indices"
  if ( not isinstance(d2, tensor) ) or (len(d2.indices) != 4):
    raise TypeError, "d2 must be a tensor with 4 indices"
  if ( not isinstance(d3, tensor) ) or (len(d3.indices) != 6):
    raise TypeError, "d3 must be a tensor with 6 indices"

  # sort the 3-body RDM's indices into 1212 ordering
  d3_indices = []
  if   indexOrder == '1212':
    d3_indices.extend(d3.indices)
  elif indexOrder == '1122':
    for i in range(3):
      d3_indices.append(d3.indices[2*i])
    for i in range(3):
      d3_indices.append(d3.indices[2*i+1])
  else:
    raise ValueError, "unexpected indexOrder parameter:  %s" %(indexOrder)

  decomp = []

  # Prepare terms from    sum (-1)^p %gamma^{i_1}_{i_4} %lambda^{i_2`i_3}_{i_5`i_6}
  for p0 in range(3):
    temp = range(3)
    del temp[p0]
    p1 = temp[0]
    p2 = temp[1]
    ti = [p0, p1, p2]
    for q0 in range(3):
      temp = range(3)
      del temp[q0]
      q1 = temp[0]
      q2 = temp[1]
      if q1 == p2 or q2 == p1:
        bi = [q0, q2, q1]
      else:
        bi = [q0, q1, q2]

      # determine the sign of the term based on the number of index permutations
      n_perm = get_num_perms(ti,bi)
      if n_perm > 1:
        raise ValueError, "n_perm = %i which is > 1.  This was unexpected." %n_perm
      sign = (-1)**n_perm

      # create the 1RDM 2RDM term
      bj = [i for i in bi]
      n_perm = get_num_perms(ti,bj)
      coeff = sign * (0.5)**n_perm
      bj = [i+3 for i in bj]
      d1_copy_0 = d1.copy()
      d1_copy_0.indices = [d3_indices[i] for i in (ti[0:1] + bj[0:1])]
      d2_copy_0 = d2.copy()
      d2_copy_0.indices = [d3_indices[i] for i in (ti[1:3] + bj[1:3])]
      decomp.append(term(coeff, [], [d1_copy_0, d2_copy_0]))

      # create the 1RDM 1RDM 1RDM terms
      bj = [i for i in bi]
      n_perm = get_num_perms(ti,bj)
      coeff = sign * (-1) * (0.5)**n_perm
      bj = [i+3 for i in bj]
      d1_copies = []
      for j in range(3):
        d1_copies.append(d1.copy())
        d1_copies[-1].indices = [d3_indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
      decomp.append(term(coeff, [], d1_copies))
      bj = bi[0:1] + bi[2:3] + bi[1:2]
      n_perm = get_num_perms(ti,bj)
      coeff = sign * (0.5)**n_perm
      bj = [i+3 for i in bj]
      d1_copies = []
      for j in range(3):
        d1_copies.append(d1.copy())
        d1_copies[-1].indices = [d3_indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
      decomp.append(term(coeff, [], d1_copies))

  # Compute terms from the sum involving only one particle density matrices
  ti = range(3)
  for bi in makePermutations(3):
    
    # Determine the number of permutations
    n_perm = get_num_perms(ti,bi)

    # Determine the sign
    sign = (-1)**n_perm

    # Compute 1RDM 1RDM 1RDM 1RDM term
    bj = [i+3 for i in bi]
    coeff = sign * (0.5)**n_perm
    d1_copies = []
    for j in range(3):
      d1_copies.append(d1.copy())
      d1_copies[-1].indices = [d3_indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
    decomp.append(term(coeff, [], d1_copies))

  # Combine like terms
  for t in decomp:
    t.tensors.sort()
  decomp.sort()
  i = 0
  while i < len(decomp)-1:
    if decomp[i].tensors == decomp[i+1].tensors:
      decomp[i+1].numConstant += decomp[i].numConstant
      del(decomp[i])
    else:
      i += 1
  termChop(decomp)

  # if original ordering was 1122, convert back to it
  if indexOrder == '1122':
    for t in decomp:
      for ten in t.tensors:
        if ten.name == d2.name:
          ten.indices = [ten.indices[0], ten.indices[2], ten.indices[1], ten.indices[3]]

  # Return the decomposition
  return decomp
def normalOrder(inTerm):
  "Returns a list of terms resulting from normal ordering the operators in inTerm."

#  if options.verbose:
#    print "converting to normal order:  %s" %(str(inTerm))

  # check that inTerm is a term
  if not isinstance(inTerm, term):
    raise TypeError, "inTerm must be of class term"

  # determine what types of operators the term contains
  has_creDesOps = False
  has_sfExOps = False
  for t in inTerm.tensors:
    if isinstance(t, creOp) or isinstance(t, desOp):
      has_creDesOps = True
    elif isinstance(t, sfExOp):
      has_sfExOps = True

  # If term has both creation/destruction operators and spin free excitation operators,
  # raise an error
  if has_creDesOps and has_sfExOps:
    raise RuntimeError, "Normal ordering not implemented when both creOp/desOp and sfExOp " + \
                        "tensors are present"

  # if the term is already normal ordered, return it unchanged
  elif inTerm.isNormalOrdered():
    outTerms = [inTerm.copy()]

  # Normal ordering for creOp/desOp
  elif has_creDesOps:

    # Separate the cre/des operators from other tensors
    ops = []
    nonOps = []
    for t in inTerm.tensors:
      if isinstance(t, creOp) or isinstance(t, desOp):
        ops.append(t.copy())
      else:
        nonOps.append(t.copy())

    # Generate all contraction pairs
    contractionPairs = []
    for i in range(len(ops)):
      iTerm = ops[i]
      for j in range(i+1,len(ops)):
        jTerm = ops[j]
        if isinstance(iTerm, desOp) and isinstance(jTerm, creOp):
          contractionPairs.append((i,j));
    #print "contractionPairs\n", contractionPairs

    # Determine maximum contraction order
    creCount = 0
    maxConOrder = 0
    for i in range(len(ops)-1,-1,-1):
      iTerm = ops[i]
      if isinstance(iTerm, creOp):
        creCount +=1
      elif isinstance(iTerm, desOp) and creCount > 0:
        maxConOrder += 1
        creCount -= 1
    del(creCount,iTerm)

    # Generate all contractions
    contractions = []
    for i in range(maxConOrder+1):
      subCons = makeTuples(i,contractionPairs)
      j = 0
      while j < len(subCons):
        creOpTags = []
        desOpTags = []
        for k in range(i):
          creOpTags.append(subCons[j][k][1])
          desOpTags.append(subCons[j][k][0])
        if allDifferent(creOpTags) and allDifferent(desOpTags):
          j += 1
        else:
          del(subCons[j])
      for j in range(len(subCons)):
        contractions.append(subCons[j])
    del(subCons,creOpTags,desOpTags,contractionPairs)
    #print "contractions:\n", contractions

    # For each contraction, generate the resulting term
    outTerms = []
    for contraction in contractions:
      conSign = 1
      deltaFuncs = []
      conIndeces = []
      subOpString = []
      subOpString.extend(ops)
      for conPair in contraction:
        index1 = ops[conPair[0]].indices[0]
        index2 = ops[conPair[1]].indices[0]
        deltaFuncs.append(kroneckerDelta([index1,index2]))
        subOpString[conPair[0]] = 'contracted'
        subOpString[conPair[1]] = 'contracted'
        for q in subOpString[conPair[0]+1:conPair[1]]:
          if not (q is 'contracted'):
            conSign *= -1
      i = 0
      while i < len(subOpString):
        if subOpString[i] is 'contracted':
          del(subOpString[i])
        else:
          i += 1
      (sortSign,sortedOps) = sortOps(subOpString)
      totalSign = conSign * sortSign
      outTensors = []
      outTensors.extend(nonOps)
      outTensors.extend(deltaFuncs)
      outTensors.extend(sortedOps)
      outTerms.append( term(totalSign * inTerm.numConstant, inTerm.constants, outTensors) )

  # Normal ordering for sfExOps
  elif has_sfExOps:

    # Make separate lists of the spin free excitation operators and other tensors
    sfExOp_list = []
    other_list = []
    for t in inTerm.tensors:
      if isinstance(t, sfExOp):
        sfExOp_list.append(t.copy())
      else:
        other_list.append(t.copy())

    # Initialize n, the number of remaining spin free excitation operators
    n = len(sfExOp_list)

    # Set the original term, with all excitation operators moved to the end, as the
    # first iteration's input term
    iter_input_terms = [term(inTerm.numConstant, inTerm.constants, other_list + sfExOp_list)]

    # Successively normal order the last two excitation operators until each term
    # has only one exitation operator left (at which point the term is normal ordered)
    while n > 1:

      # Initialize the list to hold this iteration's output terms
      iter_output_terms = []

      # For each of this iteration's input terms, produce all terms resulting from normal ordering
      # the last two excitation operators
      for t in iter_input_terms:

        # Make a list of the term's tensors that excludes the last two excitation operators
        tensors_except_last_two = []
        tensors_except_last_two.extend(t.tensors[0:-2])

        # Give short names for the last two excitation operators and their orders
        e1 = t.tensors[-2]
        e2 = t.tensors[-1]
        o1 = e1.order
        o2 = e2.order

        # Loop over the number of contractions
        for nc in range(min(o1,o2)+1):
          
          # Compute the order of excitation operator for the current number of contractions
          newOrder = o1 + o2 - nc

          # Compute all nc-tuples of index numbers from e1 and e2, as well as all permutations of 
          # the order in which the tuples may be combined to form a contraction
          perms = [0]
          if nc > 0:
            perms = makePermutations(nc)
          tups1 = makeTuples(nc, range(o1))
          tups2 = makeTuples(nc, range(o2))

          # For each contraction, compute the resulting term
          for perm in perms:
            for tup1 in tups1:
              for tup2 in tups2:

                # Initialize the term's tensor list
                tensorList = []
                tensorList.extend(tensors_except_last_two)

                # Compute the pairs of indices to be contracted
                # Example: (conPairs[0][p], conPairs[1][p]) is the pth contraction pair.
                conPairs = [[],[]]
                for i in range(nc):
                  conPairs[0].append(tup1[perm[i]])
                  conPairs[1].append(tup2[i])

                # Initialize the index list for the new excitation operator
                indexList = [False] * (2*newOrder)

                # Populate the index list for the new excitation operator
                # Also, create a kronecker delta function for each contraction pair
                for i in range(o1):
                  indexList[i] = e1.indices[i]                     # The usual one
                  if i in conPairs[0]:
                    i1 = i+o1
                    i2 = conPairs[1][conPairs[0].index(i)]
                    ind1 = e1.indices[i1]
                    ind2 = e2.indices[i2]
                    tensorList.insert(0, kroneckerDelta([ind1,ind2]))
                    indexList[i+newOrder] = e2.indices[o2+i2]
                  else:
                    indexList[i+newOrder] = e1.indices[i+o1]       # Put the usual counterpart into indexList
                count = 0                                          # in case of that e1.indices[i] is not contracted
                for i in range(o2):
                  if not (i in conPairs[1]):
                    indexList[o1+count] = e2.indices[i]
                    indexList[o1+count+newOrder] = e2.indices[i+o2]
                    count += 1

                # Ensure that all slots in the index list have been filled
                for ind in indexList:
                  if ind is False:
                    raise RuntimeError, "There is at least one unassigned index in the new spin free operator."

                # Add the new excitation operator to the tensor list
                tensorList.append(sfExOp(indexList))

                # Add the resulting term to this iteration's list of output terms
                iter_output_terms.append(term(inTerm.numConstant, inTerm.constants, tensorList))

      # Set this iteration's list of output terms as the next iteration's input terms
      iter_input_terms = iter_output_terms

      # Decrement the counter for the number of excitation operators
      n -= 1; #print "MADE OK!!!"
      
    # Set the return value as the final iteration's output terms
    outTerms = iter_output_terms

  else:
    raise RuntimeError, "Normal ordering function failed to choose what to do."

#  print "Terms after normal ordering:"
#  for t in outTerms:
#    print t

  return outTerms
def contractCoreOps_sf(inTerm):
  "Returns a list of terms resulting from normal ordering the operators in inTerm."

  # check that inTerm is a term
  if not isinstance(inTerm, term):
    raise TypeError, "inTerm must be of class term"

  # Make separate lists of the spin free excitation operators and other tensors
  sfExOp_list = []
  other_list = []
  for t in inTerm.tensors:
    if isinstance(t, sfExOp):
      sfExOp_list.append(t.copy())
    else:
      other_list.append(t.copy())

  # Initialize n, the number of remaining spin free excitation operators
  if len(sfExOp_list) != 1:
    raise RuntimeError, "terms should have single sfExOp"

  # Give short names for the excitation operator and their order
  e1 = sfExOp_list[0]
  o1 = e1.order

  # list of offset of "core" type indices
  cIndsCre = []
  cIndsDes = []
  for i in range(o1):
    if e1.indices[i].indType == (options.core_type,):
      cIndsCre.append(i)
    if e1.indices[i+o1].indType == (options.core_type,):
      cIndsDes.append(i+o1)

  # all the "core" type indices should be contracted
  nc = len(cIndsCre)
  outTerms = []
  if nc != len(cIndsDes):
    outTerms.append(term(0.0, [], []))
    return outTerms
  if nc == 0:
    outTerms = [inTerm.copy()]
    return outTerms
  newOrder = o1 - nc

  # For each contraction, compute the resulting term
  perms = makePermutations(nc)
  for perm in perms:

    # Compute the pairs of indices to be contracted
    # Example: (conPairs[0][p], conPairs[1][p]) is the pth contraction pair.
    conPairs = [[],[]]
    for i in range(nc):
      conPairs[0].append(cIndsCre[perm[i]])
      conPairs[1].append(cIndsDes[i])

    # evaluate constant prefactor
    prefactor = 1
    inds = []
    for i in range(nc):
      if i not in inds:
        inds.append(i)
        i0 = conPairs[0][i]
        i1 = conPairs[1][i] - o1
        if i1 == i0:
          prefactor *= 2
          continue
#old        if i1 in conPairs[0]:
#old          inds.append(conPairs[0].index(i1))
#old          i2 = conPairs[1][conPairs[0].index(i1)] - o1
#old          if i2 == i0:
#old            prefactor *= 2
#old            continue
#old          if i2 in conPairs[0]:
#old            inds.append(conPairs[0].index(i2))
#old            i3 = conPairs[1][conPairs[0].index(i2)] - o1
#old            if i3 == i0:
#old              prefactor *= 2
#old              continue
#old            if i3 in conPairs[0]:
#old              inds.append(conPairs[0].index(i3))
#old              i4 = conPairs[1][conPairs[0].index(i3)] - o1
#old              if i4 == i0:
#old                prefactor *= 2
#old                continue
#old              else:
#old                raise RuntimeError, "NYI:recursive algorithm1"
        while i1 in conPairs[0]:
          inds.append(conPairs[0].index(i1))
          i1 = conPairs[1][conPairs[0].index(i1)] - o1
          if i1 == i0:
            prefactor *= 2
            break
#
        
    # Initialize the term's tensor list
    tensorList = other_list[:]

    # Initialize the index list for the new excitation operator
    indexList = [False] * (2*newOrder)

    # Populate the index list for the new excitation operator
    # Also, create a kronecker delta function for each contraction pair
    count = 0
    for i1 in range(o1):
      if i1 in conPairs[0]:#create a kronecker delta
        i2 = conPairs[1][conPairs[0].index(i1)]
        ind1 = e1.indices[i1]
        ind2 = e1.indices[i2]
        tensorList.insert(0, kroneckerDelta([ind1,ind2]))
      else:               #insert a pair of indices
        indexList[count] = e1.indices[i1]
        i2 = i1+o1
#old        if i2 in conPairs[1]:
#old          i2 = conPairs[0][conPairs[1].index(i2)] + o1
#old          if i2 in conPairs[1]:
#old            i2 = conPairs[0][conPairs[1].index(i2)] + o1
#old            if i2 in conPairs[1]:
#old              i2 = conPairs[0][conPairs[1].index(i2)] + o1
#old              if i2 in conPairs[1]:
#old                i2 = conPairs[0][conPairs[1].index(i2)] + o1
#old                if i2 in conPairs[1]:
#old                  raise RuntimeError, "NYI:recursive algorithm2"
#old              else:
#old                indexList[count+newOrder] = e1.indices[i2]
#old            else:
#old              indexList[count+newOrder] = e1.indices[i2]
#old          else:
#old            indexList[count+newOrder] = e1.indices[i2]
#old        else:
#old          indexList[count+newOrder] = e1.indices[i2]
        while i2 in conPairs[1]:
          i2 = conPairs[0][conPairs[1].index(i2)] + o1
        else: indexList[count+newOrder] = e1.indices[i2]
#
        count += 1

    # Ensure that all slots in the index list have been filled
    for ind in indexList:
      if ind is False:
        raise RuntimeError, "There is at least one unassigned index in the new spin free operator."

    # Add the new excitation operator to the tensor list
    if len(indexList) != 0:
      tensorList.append(sfExOp(indexList))

    # Determine the sign
    indPairs = [[],[]]
    for i in range(o1):
      indPairs[0].append(i)
      if i in conPairs[0]:
        indPairs[1].append(conPairs[1][conPairs[0].index(i)]-o1)
      else:
        a  = e1.indices[i]
        b = indexList[indexList.index(a)+newOrder]
        #indPairs[1].append(e1.indices.index(b)-o1)#is not correct when there are duplicate indices
        indPairs[1].append(e1.indices[o1:2*o1].index(b))

    #for i in range(o1): indPairs[1].index(i)
    n_perm = get_num_perms(indPairs[0], indPairs[1])
    sign = (-1)**n_perm

    # Add the resulting term to this iteration's list of output terms
    #print term(sign*prefactor*inTerm.numConstant, inTerm.constants, tensorList)
    #print "-------------------------------------------------------------------"
    outTerms.append(term(sign*prefactor*inTerm.numConstant, inTerm.constants, tensorList))

  return outTerms
def contractCoreOps_sf(inTerm):
    "Returns a list of terms resulting from normal ordering the operators in inTerm."

    # check that inTerm is a term
    if not isinstance(inTerm, term):
        raise TypeError, "inTerm must be of class term"

    # Make separate lists of the spin free excitation operators and other tensors
    sfExOp_list = []
    other_list = []
    for t in inTerm.tensors:
        if isinstance(t, sfExOp):
            sfExOp_list.append(t.copy())
        else:
            other_list.append(t.copy())

    # Initialize n, the number of remaining spin free excitation operators
    if len(sfExOp_list) != 1:
        raise RuntimeError, "terms should have single sfExOp"

    # Give short names for the excitation operator and their order
    e1 = sfExOp_list[0]
    o1 = e1.order

    # list of offset of "core" type indices
    cIndsCre = []
    cIndsDes = []
    for i in range(o1):
        if e1.indices[i].indType == (options.core_type, ):
            cIndsCre.append(i)
        if e1.indices[i + o1].indType == (options.core_type, ):
            cIndsDes.append(i + o1)

    # all the "core" type indices should be contracted
    nc = len(cIndsCre)
    outTerms = []
    if nc != len(cIndsDes):
        outTerms.append(term(0.0, [], []))
        return outTerms
    if nc == 0:
        outTerms = [inTerm.copy()]
        return outTerms
    newOrder = o1 - nc

    # For each contraction, compute the resulting term
    perms = makePermutations(nc)
    for perm in perms:

        # Compute the pairs of indices to be contracted
        # Example: (conPairs[0][p], conPairs[1][p]) is the pth contraction pair.
        conPairs = [[], []]
        for i in range(nc):
            conPairs[0].append(cIndsCre[perm[i]])
            conPairs[1].append(cIndsDes[i])

        # evaluate constant prefactor
        prefactor = 1
        inds = []
        for i in range(nc):
            if i not in inds:
                inds.append(i)
                i0 = conPairs[0][i]
                i1 = conPairs[1][i] - o1
                if i1 == i0:
                    prefactor *= 2
                    continue
#old        if i1 in conPairs[0]:
#old          inds.append(conPairs[0].index(i1))
#old          i2 = conPairs[1][conPairs[0].index(i1)] - o1
#old          if i2 == i0:
#old            prefactor *= 2
#old            continue
#old          if i2 in conPairs[0]:
#old            inds.append(conPairs[0].index(i2))
#old            i3 = conPairs[1][conPairs[0].index(i2)] - o1
#old            if i3 == i0:
#old              prefactor *= 2
#old              continue
#old            if i3 in conPairs[0]:
#old              inds.append(conPairs[0].index(i3))
#old              i4 = conPairs[1][conPairs[0].index(i3)] - o1
#old              if i4 == i0:
#old                prefactor *= 2
#old                continue
#old              else:
#old                raise RuntimeError, "NYI:recursive algorithm1"
                while i1 in conPairs[0]:
                    inds.append(conPairs[0].index(i1))
                    i1 = conPairs[1][conPairs[0].index(i1)] - o1
                    if i1 == i0:
                        prefactor *= 2
                        break
#

# Initialize the term's tensor list
        tensorList = other_list[:]

        # Initialize the index list for the new excitation operator
        indexList = [False] * (2 * newOrder)

        # Populate the index list for the new excitation operator
        # Also, create a kronecker delta function for each contraction pair
        count = 0
        for i1 in range(o1):
            if i1 in conPairs[0]:  #create a kronecker delta
                i2 = conPairs[1][conPairs[0].index(i1)]
                ind1 = e1.indices[i1]
                ind2 = e1.indices[i2]
                tensorList.insert(0, kroneckerDelta([ind1, ind2]))
            else:  #insert a pair of indices
                indexList[count] = e1.indices[i1]
                i2 = i1 + o1
                #old        if i2 in conPairs[1]:
                #old          i2 = conPairs[0][conPairs[1].index(i2)] + o1
                #old          if i2 in conPairs[1]:
                #old            i2 = conPairs[0][conPairs[1].index(i2)] + o1
                #old            if i2 in conPairs[1]:
                #old              i2 = conPairs[0][conPairs[1].index(i2)] + o1
                #old              if i2 in conPairs[1]:
                #old                i2 = conPairs[0][conPairs[1].index(i2)] + o1
                #old                if i2 in conPairs[1]:
                #old                  raise RuntimeError, "NYI:recursive algorithm2"
                #old              else:
                #old                indexList[count+newOrder] = e1.indices[i2]
                #old            else:
                #old              indexList[count+newOrder] = e1.indices[i2]
                #old          else:
                #old            indexList[count+newOrder] = e1.indices[i2]
                #old        else:
                #old          indexList[count+newOrder] = e1.indices[i2]
                while i2 in conPairs[1]:
                    i2 = conPairs[0][conPairs[1].index(i2)] + o1
                else:
                    indexList[count + newOrder] = e1.indices[i2]
                #
                count += 1

        # Ensure that all slots in the index list have been filled
        for ind in indexList:
            if ind is False:
                raise RuntimeError, "There is at least one unassigned index in the new spin free operator."

        # Add the new excitation operator to the tensor list
        if len(indexList) != 0:
            tensorList.append(sfExOp(indexList))

        # Determine the sign
        indPairs = [[], []]
        for i in range(o1):
            indPairs[0].append(i)
            if i in conPairs[0]:
                indPairs[1].append(conPairs[1][conPairs[0].index(i)] - o1)
            else:
                a = e1.indices[i]
                b = indexList[indexList.index(a) + newOrder]
                #indPairs[1].append(e1.indices.index(b)-o1)#is not correct when there are duplicate indices
                indPairs[1].append(e1.indices[o1:2 * o1].index(b))

        #for i in range(o1): indPairs[1].index(i)
        n_perm = get_num_perms(indPairs[0], indPairs[1])
        sign = (-1)**n_perm

        # Add the resulting term to this iteration's list of output terms
        #print term(sign*prefactor*inTerm.numConstant, inTerm.constants, tensorList)
        #print "-------------------------------------------------------------------"
        outTerms.append(
            term(sign * prefactor * inTerm.numConstant, inTerm.constants,
                 tensorList))

    return outTerms
def normalOrder(inTerm):
    "Returns a list of terms resulting from normal ordering the operators in inTerm."

    #  if options.verbose:
    #    print "converting to normal order:  %s" %(str(inTerm))

    # check that inTerm is a term
    if not isinstance(inTerm, term):
        raise TypeError, "inTerm must be of class term"

    # determine what types of operators the term contains
    has_creDesOps = False
    has_sfExOps = False
    for t in inTerm.tensors:
        if isinstance(t, creOp) or isinstance(t, desOp):
            has_creDesOps = True
        elif isinstance(t, sfExOp):
            has_sfExOps = True

    # If term has both creation/destruction operators and spin free excitation operators,
    # raise an error
    if has_creDesOps and has_sfExOps:
        raise RuntimeError, "Normal ordering not implemented when both creOp/desOp and sfExOp " + \
                            "tensors are present"

    # if the term is already normal ordered, return it unchanged
    elif inTerm.isNormalOrdered():
        outTerms = [inTerm.copy()]

    # Normal ordering for creOp/desOp
    elif has_creDesOps:

        # Separate the cre/des operators from other tensors
        ops = []
        nonOps = []
        for t in inTerm.tensors:
            if isinstance(t, creOp) or isinstance(t, desOp):
                ops.append(t.copy())
            else:
                nonOps.append(t.copy())

        # Generate all contraction pairs
        contractionPairs = []
        for i in range(len(ops)):
            iTerm = ops[i]
            for j in range(i + 1, len(ops)):
                jTerm = ops[j]
                if isinstance(iTerm, desOp) and isinstance(jTerm, creOp):
                    contractionPairs.append((i, j))
        #print "contractionPairs\n", contractionPairs

        # Determine maximum contraction order
        creCount = 0
        maxConOrder = 0
        for i in range(len(ops) - 1, -1, -1):
            iTerm = ops[i]
            if isinstance(iTerm, creOp):
                creCount += 1
            elif isinstance(iTerm, desOp) and creCount > 0:
                maxConOrder += 1
                creCount -= 1
        del (creCount, iTerm)

        # Generate all contractions
        contractions = []
        for i in range(maxConOrder + 1):
            subCons = makeTuples(i, contractionPairs)
            j = 0
            while j < len(subCons):
                creOpTags = []
                desOpTags = []
                for k in range(i):
                    creOpTags.append(subCons[j][k][1])
                    desOpTags.append(subCons[j][k][0])
                if allDifferent(creOpTags) and allDifferent(desOpTags):
                    j += 1
                else:
                    del (subCons[j])
            for j in range(len(subCons)):
                contractions.append(subCons[j])
        del (subCons, creOpTags, desOpTags, contractionPairs)
        #print "contractions:\n", contractions

        # For each contraction, generate the resulting term
        outTerms = []
        for contraction in contractions:
            conSign = 1
            deltaFuncs = []
            conIndeces = []
            subOpString = []
            subOpString.extend(ops)
            for conPair in contraction:
                index1 = ops[conPair[0]].indices[0]
                index2 = ops[conPair[1]].indices[0]
                deltaFuncs.append(kroneckerDelta([index1, index2]))
                subOpString[conPair[0]] = 'contracted'
                subOpString[conPair[1]] = 'contracted'
                for q in subOpString[conPair[0] + 1:conPair[1]]:
                    if not (q is 'contracted'):
                        conSign *= -1
            i = 0
            while i < len(subOpString):
                if subOpString[i] is 'contracted':
                    del (subOpString[i])
                else:
                    i += 1
            (sortSign, sortedOps) = sortOps(subOpString)
            totalSign = conSign * sortSign
            outTensors = []
            outTensors.extend(nonOps)
            outTensors.extend(deltaFuncs)
            outTensors.extend(sortedOps)
            outTerms.append(
                term(totalSign * inTerm.numConstant, inTerm.constants,
                     outTensors))

    # Normal ordering for sfExOps
    elif has_sfExOps:

        # Make separate lists of the spin free excitation operators and other tensors
        sfExOp_list = []
        other_list = []
        for t in inTerm.tensors:
            if isinstance(t, sfExOp):
                sfExOp_list.append(t.copy())
            else:
                other_list.append(t.copy())

        # Initialize n, the number of remaining spin free excitation operators
        n = len(sfExOp_list)

        # Set the original term, with all excitation operators moved to the end, as the
        # first iteration's input term
        iter_input_terms = [
            term(inTerm.numConstant, inTerm.constants,
                 other_list + sfExOp_list)
        ]

        # Successively normal order the last two excitation operators until each term
        # has only one exitation operator left (at which point the term is normal ordered)
        while n > 1:

            # Initialize the list to hold this iteration's output terms
            iter_output_terms = []

            # For each of this iteration's input terms, produce all terms resulting from normal ordering
            # the last two excitation operators
            for t in iter_input_terms:

                # Make a list of the term's tensors that excludes the last two excitation operators
                tensors_except_last_two = []
                tensors_except_last_two.extend(t.tensors[0:-2])

                # Give short names for the last two excitation operators and their orders
                e1 = t.tensors[-2]
                e2 = t.tensors[-1]
                o1 = e1.order
                o2 = e2.order
                #lsh spin
                s1 = e1.spin
                s2 = e2.spin
                spin1f = s1[0][:]
                spin1e = s1[1][:]
                spin2f = s2[0][:]
                spin2e = s2[1][:]
                shift = max(max(spin1f), max(spin1e))

                # Loop over the number of contractions
                for nc in range(min(o1, o2) + 1):

                    # Compute the order of excitation operator for the current number of contractions
                    newOrder = o1 + o2 - nc

                    # Compute all nc-tuples of index numbers from e1 and e2, as well as all permutations of
                    # the order in which the tuples may be combined to form a contraction
                    perms = [0]
                    if nc > 0:
                        perms = makePermutations(nc)
                    tups1 = makeTuples(nc, range(o1))
                    tups2 = makeTuples(nc, range(o2))

                    #print('perms=',perms)
                    #print('tups1=',tups1)
                    #print('tups2=',tups2)

                    # For each contraction, compute the resulting term
                    for perm in perms:
                        for tup1 in tups1:
                            for tup2 in tups2:

                                # Initialize the term's tensor list
                                tensorList = []
                                tensorList.extend(tensors_except_last_two)

                                # Compute the pairs of indices to be contracted
                                # Example: (conPairs[0][p], conPairs[1][p]) is the pth contraction pair.
                                conPairs = [[], []]
                                for i in range(nc):
                                    conPairs[0].append(tup1[perm[i]])
                                    conPairs[1].append(tup2[i])

                                # Initialize the index list for the new excitation operator
                                indexList = [False] * (2 * newOrder)

                                # Populate the index list for the new excitation operator
                                # Also, create a kronecker delta function for each contraction pair
                                for i in range(o1):
                                    indexList[i] = e1.indices[
                                        i]  # The usual one
                                    if i in conPairs[0]:
                                        i1 = i + o1
                                        i2 = conPairs[1][conPairs[0].index(i)]
                                        ind1 = e1.indices[i1]
                                        ind2 = e2.indices[i2]
                                        tensorList.insert(
                                            0, kroneckerDelta([ind1, ind2]))
                                        indexList[i +
                                                  newOrder] = e2.indices[o2 +
                                                                         i2]
                                    else:
                                        indexList[i + newOrder] = e1.indices[
                                            i +
                                            o1]  # Put the usual counterpart into indexList
                                count = 0  # in case of that e1.indices[i] is not contracted
                                for i in range(o2):
                                    if not (i in conPairs[1]):
                                        indexList[o1 + count] = e2.indices[i]
                                        indexList[o1 + count +
                                                  newOrder] = e2.indices[i +
                                                                         o2]
                                        count += 1

                                #lsh parity test
                                #print 'conPairs=', conPairs
                                parity_n = 0
                                o1_l = [i for i in range(o1)]
                                o2_l = [i for i in range(o2)]
                                for i in range(len(conPairs[0])):
                                    p1 = conPairs[0][i]
                                    p2 = conPairs[1][i]
                                    parity_n += o1_l.index(p1)
                                    #print 'parity = ', p1, p2, o1_l, o2_l, parity_n
                                    parity_n += o2_l.index(p2)
                                    #print 'parity = ', p1, p2, o1_l, o2_l, parity_n
                                    o1_l.remove(p1)
                                    o2_l.remove(p2)
                                parity = (-1)**(parity_n)
                                #parity *= 2**(len(conPairs[0]))

                                #lsh spin test
                                spin_dict = {}
                                for i in range(len(conPairs[0])):
                                    p1 = conPairs[0][i]
                                    p2 = conPairs[1][i]
                                    spin_dict[spin2f[p2]] = spin1e[p1]

                                spin_new_f = spin1f[:]
                                #print 'spin 1 = ', spin_new_f
                                for i in range(len(spin2f)):
                                    if i not in conPairs[1]:
                                        spin_new_f.append(spin2f[i] + shift +
                                                          1)
                                #print 'spin 2 = ', spin_new_f

                                spin_new_e = []
                                for i in range(len(spin1e)):
                                    if i not in conPairs[0]:
                                        spin_new_e.append(spin1e[i])

                                #print 'spin 3 = ', spin_new_e
                                for i in spin2e:
                                    if i not in spin_dict:
                                        spin_new_e.append(i + shift + 1)
                                    else:
                                        spin_new_e.append(spin_dict[i])
                                #print 'spin 4 = ', spin_new_e

                                # Ensure that all slots in the index list have been filled
                                for ind in indexList:
                                    if ind is False:
                                        raise RuntimeError, "There is at least one unassigned index in the new spin free operator."
                                #print 'spin= ', spin_new_f, spin_new_e
                                # Add the new excitation operator to the tensor list
                                tensorList.append(
                                    sfExOp(indexList,
                                           spin=[spin_new_f, spin_new_e]))

                                # Add the resulting term to this iteration's list of output terms
                                iter_output_terms.append(
                                    term(parity * inTerm.numConstant,
                                         inTerm.constants, tensorList))

            # Set this iteration's list of output terms as the next iteration's input terms
            iter_input_terms = iter_output_terms

            # Decrement the counter for the number of excitation operators
            n -= 1
            #print "MADE OK!!!"

        # Set the return value as the final iteration's output terms
        outTerms = iter_output_terms

    else:
        raise RuntimeError, "Normal ordering function failed to choose what to do."

#  print "Terms after normal ordering:"
#  for t in outTerms:
#    print t

    return outTerms