Determines if problem_name specifies a copy and/or reversal.

  Args:
    name: str, problem name, possibly with suffixes.

  Returns:
    ProblemSpec: namedtuple with ["base_name", "was_reversed", "was_copy"]

  Raises:
    ValueError if name contains multiple suffixes of the same type
      ('_rev' or '_copy'). One of each is ok.

def parse_problem_name(name):
  """Determines if problem_name specifies a copy and/or reversal.

  Args:
    name: str, problem name, possibly with suffixes.

  Returns:
    ProblemSpec: namedtuple with ["base_name", "was_reversed", "was_copy"]

  Raises:
    ValueError if name contains multiple suffixes of the same type
      ('_rev' or '_copy'). One of each is ok.
  """
  # Recursively strip tags until we reach a base name.
  if name.endswith("_rev"):
    base, was_reversed, was_copy = parse_problem_name(name[:-4])
    if was_reversed:
      # duplicate rev
      raise ValueError(
          "Invalid problem name %s: multiple '_rev' instances" % name)
    return ProblemSpec(base, True, was_copy)
  elif name.endswith("_copy"):
    base, was_reversed, was_copy = parse_problem_name(name[:-5])
    if was_copy:
      raise ValueError(
          "Invalid problem_name %s: multiple '_copy' instances" % name)
    return ProblemSpec(base, was_reversed, True)
  else:
    return ProblemSpec(name, False, False)