def entropy(data=None, prob=None, tol=1e-5):
    '''
    given a probability distribution (prob) or an interable of symbols (data) compute and
    return its entropy

    inputs:
    ------
        data:       iterable of symbols

        prob:       iterable with probabilities

        tol:        if prob is given, 'entropy' checks that the sum is about 1.
                    It raises an error if abs(sum(prob)-1) >= tol
    '''

    if prob is None and data is None:
        raise ValueError("%s.entropy requires either 'prob' or 'data' to be defined" % __name__)

    if prob is not None and data is not None:
        raise ValueError("%s.entropy requires only 'prob' or 'data to be given but not both" % __name__)

    if prob is not None and not isinstance(prob, np.ndarray):
        raise TypeError("'entropy' in '%s' needs 'prob' to be an ndarray" % __name__)

    if prob is not None and abs(prob.sum()-1) > tol:
        raise ValueError("parameter 'prob' in '%s.entropy' should sum to 1" % __name__)

    if data is not None:
        prob = symbols_to_prob(data).prob()

    # compute the log2 of the probability and change any -inf by 0s
    logProb = np.log2(prob)
    logProb[logProb == -np.inf] = 0

    # return dot product of logProb and prob
    return -float(np.dot(prob, logProb))
given a probability distribution (prob) or an interable of symbols (data) compute and
    return its entropy

    inputs:
    ------
        data:       iterable of symbols

        prob:       iterable with probabilities

        tol:        if prob is given, 'entropy' checks that the sum is about 1.
                    It raises an error if abs(sum(prob)-1) >= tol