import string
from typing import Counter
import math


def entropy(data):
    """
    This computes an approximation of the entropy in the given string. This is
    used to select the 'best' name of several possible names. The reason we use
    this and not just length is that in some cases (like sequences from
    structures) the name will be very long because it contains the sequence
    itself. For example:

    RNA (5'-R(*GP*UP*GP*GP*UP*CP*UP*GP*AP*UP*GP*AP*GP*GP*CP*C)-3') from synthetic construct (PDB 3D0M, chain X)

    This is not a useful name, but it is very long. Thus we do not want it.
    What we are generally after is something with the most information (to a
    human) in it. We get at this by using entropy as a guide. To make sure this
    computes a useful entropy we restrict the string to only the letters and
    numbers while ignoring case. This should provide a clean measure of an
    informative string for a person.

    Parameters
    ----------
    data : str
        A string to get the entropy for.

    Returns
    -------
    entropy : float
        The entropy of the string.
    """

    if len(data) <= 1:
        return 0
    allowed_characters = set(string.ascii_lowercase + string.digits)
    valid = list(d.lower() for d in data if d.lower() in allowed_characters)
    counts = Counter(valid)
    probs = [float(c) / len(valid) for c in counts.values() if c > 0]
    return sum(-1 * p * math.log(p, 2) for p in probs)