Returns a set of proteins with redundant peptide evidence.

    After removing the redundant proteins from the "protToPeps" and "pepToProts"
    mapping, all remaining proteins have at least one unique peptide. The
    remaining proteins are a "minimal" set of proteins that are able to explain
    all peptides. However, this is not guaranteed to be the optimal solution
    with the least number of proteins. In addition it is possible that multiple
    solutions with the same number of "minimal" proteins exist.

    Procedure for finding the redundant proteins:
    1.  Generate a list of proteins that do not contain any unique peptides, a
        unique peptide has exactly one protein entry in "pepToProts".
    2.  Proteins are first sorted in ascending order of the number of peptides.
        Proteins with an equal number of peptides are sorted in descending order
            of their sorted peptide frequencies (= proteins per peptide).
        If two proteins are still equal, they are sorted alpha numerical in
        descending order according to their protein names. For example in the
        case of a tie between proteins "A" and "B", protein "B" would be
        removed.
    3.  Parse this list of sorted non unique proteins;
        If all its peptides have a frequency value of greater 1;
        mark the protein as redundant; remove its peptides from the peptide
        frequency count, continue with the next entry.
    4.  Return the set of proteins marked as redundant.

    :param pepToProts: dict, for each peptide (=key) contains a set of parent
        proteins (=value). For Example {peptide: {protein, ...}, ...}
    :param protToPeps: dict, for each protein (=key) contains a set of
        associated peptides (=value). For Example {protein: {peptide, ...}, ...}
    :param proteins: iterable, proteins that are tested for being redundant. If
        None all proteins in "protToPeps" are parsed.
    :returns: a set of redundant proteins, i.e. proteins that are not necessary
        to explain all peptides

def _findRedundantProteins(protToPeps, pepToProts, proteins=None):
    """Returns a set of proteins with redundant peptide evidence.

    After removing the redundant proteins from the "protToPeps" and "pepToProts"
    mapping, all remaining proteins have at least one unique peptide. The
    remaining proteins are a "minimal" set of proteins that are able to explain
    all peptides. However, this is not guaranteed to be the optimal solution
    with the least number of proteins. In addition it is possible that multiple
    solutions with the same number of "minimal" proteins exist.

    Procedure for finding the redundant proteins:
    1.  Generate a list of proteins that do not contain any unique peptides, a
        unique peptide has exactly one protein entry in "pepToProts".
    2.  Proteins are first sorted in ascending order of the number of peptides.
        Proteins with an equal number of peptides are sorted in descending order
            of their sorted peptide frequencies (= proteins per peptide).
        If two proteins are still equal, they are sorted alpha numerical in
        descending order according to their protein names. For example in the
        case of a tie between proteins "A" and "B", protein "B" would be
        removed.
    3.  Parse this list of sorted non unique proteins;
        If all its peptides have a frequency value of greater 1;
        mark the protein as redundant; remove its peptides from the peptide
        frequency count, continue with the next entry.
    4.  Return the set of proteins marked as redundant.

    :param pepToProts: dict, for each peptide (=key) contains a set of parent
        proteins (=value). For Example {peptide: {protein, ...}, ...}
    :param protToPeps: dict, for each protein (=key) contains a set of
        associated peptides (=value). For Example {protein: {peptide, ...}, ...}
    :param proteins: iterable, proteins that are tested for being redundant. If
        None all proteins in "protToPeps" are parsed.
    :returns: a set of redundant proteins, i.e. proteins that are not necessary
        to explain all peptides
    """
    if proteins is None:
        proteins = viewkeys(protToPeps)

    pepFrequency = _getValueCounts(pepToProts)
    protPepCounts = _getValueCounts(protToPeps)

    getCount = operator.itemgetter(1)
    getProt = operator.itemgetter(0)

    #TODO: quick and dirty solution
    #NOTE: add a test for merged proteins
    proteinTuples = list()
    for protein in proteins:
        if isinstance(protein, tuple):
            proteinTuples.append(protein)
        else:
            proteinTuples.append(tuple([protein]))

    sort = list()
    for protein in sorted(proteinTuples, reverse=True):
        if len(protein) == 1:
            protein = protein[0]

        protPepFreq = [pepFrequency[pep] for pep in protToPeps[protein]]
        if min(protPepFreq) > 1:
            sortValue = (len(protPepFreq)*-1, sorted(protPepFreq, reverse=True))
            sort.append((protein, sortValue))
    sortedProteins = map(getProt, sorted(sort, key=getCount, reverse=True))

    redundantProteins = set()
    for protein in sortedProteins:
        for pep in protToPeps[protein]:
            if pepFrequency[pep] <= 1:
                break
        else:
            protPepFrequency = Counter(protToPeps[protein])
            pepFrequency.subtract(protPepFrequency)
            redundantProteins.add(protein)
    return redundantProteins