import collections


def generalized_jaccard_similarity_score(y_true, y_pred):
    """ Generalized Jaccard similarity score

    The Generalized Jaccard similarity [1] is an extension of the Jaccard similarity coefficient.
    It handles multisets as input variables.

    Parameters
    ----------
    y_true : 1d array-like, or label indicator array / sparse matrix
        Ground truth (correct) labels.

    y_pred : 1d array-like, or label indicator array / sparse matrix
        Predicted labels, as returned by a classifier.

    Returns
    -------
    score : float

    References
    ----------
    . [1] `Wikipedia entry for the Jaccard index
           <https://en.wikipedia.org/wiki/Jaccard_index#Generalized_Jaccard_similarity_and_distance>`_

    """

    min_count = 0
    max_count = 0

    counter_y_true = collections.Counter(y_true)
    counter_y_pred = collections.Counter(y_pred)

    # union of all elements from y_true and y_pred
    elements = set(y_true).union(set(y_pred))

    for element in elements:
        max_count += max(counter_y_true[element], counter_y_pred[element])
        min_count += min(counter_y_true[element], counter_y_pred[element])

    # could happen only if both collections are empty
    if max_count == 0:
        return 0.0

    return min_count / float(max_count)