def sorted_row_list_difference(expected, actual, key_length):
  """Finds elements in only one or the other of two, sorted input lists.

  Returns a three-element tuple of lists. The first list contains
  those elements in the "expected" list but not in the "actual"
  list, the second contains those elements in the "actual" list but
  not in the "expected" list, the third contains rows that are
  different despite having the same primary key.
  """
  # adapted from unittest.
  missing = []
  unexpected = []
  different = []

  expected, actual = iter(expected), iter(actual)
  enext = expected.next
  anext = actual.next
  try:
    e, a = enext(), anext()
    while True:
      if a == e:
        e, a = enext(), anext()
        continue

      ekey, akey = e[:key_length], a[:key_length]

      if ekey < akey:
        missing.append(e)
        e = enext()
      elif ekey > akey:
        unexpected.append(a)
        a = anext()
      else:
        different.append((a, e))
        e, a = enext(), anext()
  except StopIteration:
    missing.extend(expected)
    unexpected.extend(actual)

  return missing, unexpected, different