from typing import Iterable
from typing import Optional


def effective_sample_size(
    weights: Iterable[float],
    total_weight: Optional[float] = None,
) -> float:
    """Computes the "effective sample size" of the given weights

    This value represents how "healthy" the underlying samples are. The lower
    this value, the fewer "real samples" are represented. As in, the closer
    this comes to zero, the more degenerated the samples are.

    See `https://en.wikipedia.org/wiki/Effective_sample_size`

    :param weights: the weights of the samples
    :param total_weight: total weight of all samples, requires extra computation if not given
    :return: the effective sample size of ``weights``
    """
    if total_weight is None:
        total_weight = sum(weights)

    assert total_weight and total_weight > 0  # for mypy

    return pow(total_weight, 2) / sum(pow(w, 2) for w in weights)