# Copyright 2026 SigmaSRC, Inc. All Rights Reserved.

"""Poliwall utilities."""

integer = (int,)

def optrange(lo, hi):
    """Compute the optimal set of value:mask pairs for a range.

    The arguments should describe an inclusive range of integers [lo, hi].

    A value:mask pair (v, n) specifies the set of all integers x such that

        x & (2**n - 1) == 0

    Note that this is different from the conventional value/mask pairs
    where mask is the number of bits from the left.  We count bits
    from the right so that the same algorithm can be used for 32-bit
    IP addresses and for 48-bit MAC (hardware) addresses.  The
    conversion should be trivial by subtracting from 32 or 48.

    The purpose of this function is to compute the minimal set of
    value:mask pairs so that the set of integers defined by the union
    of the sets specified by the value:mask pairs is equal to the
    inclusive range [lo, hi].

    This is a generator.

    """
    assert isinstance(lo, integer) and isinstance(hi, integer)
    while lo <= hi:
        m = 1
        i = 0
        while lo&m == 0 and lo|m <= hi:
            i += 1
            m = m+m+1
        yield lo, i
        lo += 2**i

def optset(s):
    """Given a set of integers, compute an optimal set of value:mask pairs.

    This is like optrange(), except that the argument is an arbitrary
    set of integers which must be matched.  We'll need to sort the list.

    If the argument has an optset() method, call that instead.

    This is a generator.

    """
    # Check for optset method
    optset_method = getattr(s, "optset", None)
    if optset_method is not None:
        for vm in optset_method():
            yield vm
        return

    # Sort the input set
    items = list(s)
    items.sort()
    ilo = 0
    iend = len(items) - 1 # inclusive

    # Find consecutive ranges
    while ilo <= iend:
        ihi = ilo
        lo = hi = items[ilo]
        assert isinstance(lo, integer)
        # Find maximum mask that fits
        while ihi < iend and hi <= items[ihi+1] <= hi+1:
            ihi += 1
            hi = items[ihi]
            assert isinstance(hi, integer)
        # Call optrange and pass through whatever it returns
        for vm in optrange(lo, hi):
            yield vm
        ilo = ihi + 1
