docs: def shortenIDs(doc, prefix, unprotectedElements=None):
      """
      Shortens ID names used in the document. ID names referenced the most often are assigned the
      shortest ID names.
      If the list unprotectedElements is provided, only IDs from this list will be shortened.
  
      Returns the number of bytes saved by shortening ID names in the document.
      """
      num = 0
  
      identifiedElements = findElementsWithId(doc.documentElement)
      if unprotectedElements is None:
          unprotectedElements = identifiedElements
      referencedIDs = findReferencedElements(doc.documentElement)
  
      # Make idList (list of idnames) sorted by reference count
      # descending, so the highest reference count is first.
      # First check that there's actually a defining element for the current ID name.
      # (Cyn: I've seen documents with #id references but no element with that ID!)
      idList = [(len(referencedIDs[rid]), rid) for rid in referencedIDs
                if rid in unprotectedElements]
      idList.sort(reverse=True)
      idList = [rid for count, rid in idList]
  
      # Add unreferenced IDs to end of idList in arbitrary order
      idList.extend([rid for rid in unprotectedElements if rid not in idList])
  
      curIdNum = 1
  
      for rid in idList:
          curId = intToID(curIdNum, prefix)
          # First make sure that *this* element isn't already using
          # the ID name we want to give it.
          if curId != rid:
              # Then, skip ahead if the new ID is already in identifiedElement.
              while curId in identifiedElements:
                  curIdNum += 1
                  curId = intToID(curIdNum, prefix)
              # Then go rename it.
              num += renameID(doc, rid, curId, identifiedElements, referencedIDs)
          curIdNum += 1
  
      return num