arxpy/bitvector/operation.py
class BvOr(Operation):
    """Bitwise OR (logical disjunction) operation.

    It overrides the operator | and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvOr
        >>> BvOr(Constant(5, 8), Constant(3, 8))
        0x07
        >>> BvOr(Constant(5, 8), 3)
        0x07
        >>> Constant(5, 8) | 3
        0x07
        >>> Variable("x", 8) | Variable("y", 8)
        x | y

    """

    arity = [2, 0]
    is_symmetric = True
    is_simple = True
    infix_symbol = "|"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y):
            """OR operation when both operands are int."""
            return x | y

        zero = core.Constant(0, x.width)
        allones = BvNot(zero)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y)), x.width)
        elif x == allones or y == allones:
            return allones
        elif x == zero:
            return y
        elif y == zero:
            return x
        elif x == y:
            return x
        elif x == BvNot(y):
            return allones

    def _simplify(self, *args, **kwargs):
        compatible_terms = [
            lambda x: x,
            lambda x: BvNot(x)
        ]

        return self._binary_symmetric_simplification(compatible_terms)
arxpy/bitvector/operation.py
class BvUrem(Operation):
    """Unsigned remainder (modulus) operation.

    Usage:

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvUrem
        >>> BvUrem(Constant(0x0d, 8), Constant(3, 8))
        0x01
        >>> BvUrem(Constant(0x0d, 8), 3)
        0x01
        >>> Constant(0x0d, 8) % 3
        0x01
        >>> Variable("x", 8) % Variable("y", 8)
        x % y

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True
    infix_symbol = "%"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y):
            """Remainder operation when both operands are int."""
            assert y != 0
            return x % y

        zero = core.Constant(0, x.width)
        one = core.Constant(1, x.width)

        assert y != zero

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y)), x.width)
        elif x == y or x == zero or y == one:
            return zero
arxpy/bitvector/operation.py
class BvLshr(Operation):
    """Logical right shift operation.

    It overrides >> and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvLshr
        >>> BvLshr(Constant(0b10001, 5), Constant(1, 5))
        0b01000
        >>> BvLshr(Constant(0b10001, 5), 1)
        0b01000
        >>> Constant(0b10001, 5) >> 1
        0b01000
        >>> Variable("x", 8) >> Variable("y", 8)
        x >> y

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True
    infix_symbol = ">>"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y):
            """Logical right shift operation when both operands are int."""
            return x >> y

        zero = core.Constant(0, x.width)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y)), x.width)
        elif isinstance(y, core.Constant) and y >= x.width:
            return zero
        elif x == zero or y == zero:
            return x
        elif isinstance(x, BvLshr) and isinstance(x.args[1], core.Constant) \
                and isinstance(y, core.Constant):
            r = min(x.args[0].width, int(x.args[1]) + int(y))
            return BvLshr(x.args[0], r)
arxpy/bitvector/operation.py
class Extract(Operation):
    """Extraction of bits.

    ``Extract(t, i, j)`` extracts the bits from position ``i`` down
    position ``j`` (end points included, position 0 corresponding
    to the least significant bit).

    It overrides the operation [], that is, ``Extract(t, i, j)``
    is equivalent to ``t[i:j]``.

    Note that the indices can be omitted when they point the most
    significant bit or the least significant bit.
    For example, if ``t`` is a bit-vector of length ``n``,
    then ``t[n-1:j] = t[:j]`` and ``t[i:0] = t[i:]``

    Warning:
        In python, given a list ``l``, ``l[i:j]`` denotes the elements
        from position ``i`` up to (but no included) position ``j``.
        Note that with bit-vectors, the order of the arguments is
        swapped and both end points are included.

        For example, for a given list ``l`` and bit-vector ``t``,
        ``l[0:1] == l[0]`` and ``t[1:0] == (t[0], t[1])``.

    ::

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import Extract
        >>> Extract(Constant(0b11100, 5), 4, 2,)
        0b111
        >>> Constant(0b11100, 5)[4:2]
        0b111
        >>> Variable("x", 8)[4:2]
        x[4:2]
        >>> Variable("x", 8)[7:0]
        x

    """

    arity = [1, 2]
    is_symmetric = False
    operand_types = [core.Term, int, int]

    @classmethod
    def condition(cls, t, i, j):
        return t.width > i >= j

    @classmethod
    def output_width(cls, t, i, j):
        return i - j + 1

    @classmethod
    def eval(cls, x, i, j):
        def doit(x, i, j):
            """Extract from x[i] down x[j] from a constant x."""
            bin_repr = x.bin()
            prefix, value = bin_repr[:2], bin_repr[2:]
            n = x.width
            value = value[n - 1 - i:n - j]  # e.g.: i=n-1, j=0
            return int(prefix + value, 2)

        if isinstance(x, core.Constant):
            return core.Constant(doit(x, i, j), cls.output_width(x, i, j))
        elif i == x.width - 1 and j == 0:
            return x
        elif isinstance(x, Extract):
            # x[3:1][2] = (x3 x2 x1)[2] = x3 = x[3]
            offset = x.args[2]
            return Extract(x.args[0], i + offset, j + offset)
        elif isinstance(x, Concat):
            if i <= x.args[1].width - 1:
                # 4-bit x, y: concat(x, y)[3:] = y[3:]
                return Extract(x.args[1], i, j)
            elif j >= x.args[1].width:
                # 4-bit x, y: concat(x, y)[:5] = x[:1]
                offset = x.args[1].width
                return Extract(x.args[0], i - offset, j - offset)
        elif isinstance(x, (BvShl, RotateLeft)) and \
                isinstance(x.args[1], (int, core.Constant)) and x.args[1] <= j:
            # (x << 1)[:2] = x[n-2: 1]
            offset = int(x.args[1])
            return Extract(x.args[0], i - offset, j - offset)
        elif isinstance(x, (BvLshr, RotateRight)) and \
                isinstance(x.args[1], (int, core.Constant)) and i < x.width - x.args[1]:
            # (x >> 1)[n-3:] = x[n-2: 1]
            offset = int(x.args[1])
            return Extract(x.args[0], i + offset, j + offset)
arxpy/bitvector/core.py
def bitvectify(t, width):
    """Convert the argument *t* to a bit-vector of bit-width *width*.

        >>> from arxpy.bitvector.core import bitvectify
        >>> print(bitvectify(0, 8).vrepr())
        Constant(0b00000000, width=8)
        >>> print(bitvectify("x", 8).vrepr())
        Variable('x', width=8)

    """
    if isinstance(t, int):
        return Constant(t, width)
    elif isinstance(t, str):
        return Variable(t, width)
    elif isinstance(t, Term):
        assert t.width == width
        return t
    else:
        msg = "cannot convert '{}' to a bit-vector"
        raise TypeError(msg.format(type(t).__name__))
arxpy/bitvector/operation.py
class BvComp(Operation):
    """Equality operator.

    Provides Automatic Constant Conversion. See `Operation` for more
    information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvComp
        >>> BvComp(Constant(1, 8), Constant(2, 8))
        0b0
        >>> BvComp(Constant(1, 8), 2)
        0b0
        >>> BvComp(Constant(1, 8), Variable("y", 8))
        0x01 == y

    The operator == is used for exact structural equality testing and
    it returns either True or False. On the other hand, BvComp
    performs symbolic equality testing and it leaves the relation unevaluated
    if it cannot prove the objects are equal (or unequal).

        >>> Variable("x", 8) == Variable("y", 8)
        False
        >>> BvComp(Variable("x", 8), Variable("y", 8))  # symbolic equality
        x == y

    """

    arity = [2, 0]
    is_symmetric = True
    is_simple = True
    infix_symbol = "=="

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return 1

    @classmethod
    def eval(cls, x, y):
        zero = core.Constant(0, 1)
        one = core.Constant(1, 1)

        if x is y:
            return one
        elif isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return one if x.val == y.val else zero
arxpy/bitvector/operation.py
class BvShl(Operation):
    """Shift left operation.

    It overrides << and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvShl
        >>> BvShl(Constant(0b10001, 5), Constant(1, 5))
        0b00010
        >>> BvShl(Constant(0b10001, 5), 1)
        0b00010
        >>> Constant(0b10001, 5) << 1
        0b00010
        >>> Variable("x", 8) << Variable("y", 8)
        x << y

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True
    infix_symbol = "<<"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y, width):
            """Shift left operation when both operands are int."""
            return (x << y) % (2 ** width)

        zero = core.Constant(0, x.width)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y), x.width), x.width)
        elif isinstance(y, core.Constant) and y >= x.width:
            return zero
        elif x == zero or y == zero:
            return x
        elif isinstance(x, BvShl) and isinstance(x.args[1], core.Constant) \
                and isinstance(y, core.Constant):
            # prevent out of bound
            r = min(x.args[0].width, int(x.args[1]) + int(y))
            return BvShl(x.args[0], r)
arxpy/bitvector/operation.py
class BvAdd(Operation):
    """Modular addition operation.

    It overrides the operator + and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvAdd
        >>> BvAdd(Constant(1, 8), Constant(2, 8))
        0x03
        >>> BvAdd(Constant(1, 8), 2)
        0x03
        >>> Constant(1, 8) + 2
        0x03
        >>> Variable("x", 8) + Variable("y", 8)
        x + y

    """

    arity = [2, 0]
    is_symmetric = True
    is_simple = True
    infix_symbol = "+"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y, width):
            """Modular addition when both operands are integers."""
            return (x + y) % (2 ** width)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y), x.width), x.width)

        zero = core.Constant(0, x.width)

        if x == zero:
            return y
        elif y == zero:
            return x
        elif x == BvNeg(y):
            return zero
        elif isinstance(x, BvSub):  # (x0 - x1) + y
            if x.args[1] == y:
                return x.args[0]
        elif isinstance(y, BvSub):  # x + (y0 - y1)
            if y.args[1] == x:
                return y.args[0]

    def _simplify(self, *args, **kwargs):
        compatible_terms = [
            lambda x: BvNeg(x)
        ]

        return self._binary_symmetric_simplification(compatible_terms)
arxpy/bitvector/extraop.py
class PopCountSum3(operation.Operation):
    """Count the number of 1's of three bit-vectors.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.extraop import PopCountSum3
        >>> PopCountSum3(Constant(0b000, 3), Constant(0b001, 3), Constant(0b011, 3))
        0x3
        >>> PopCountSum3(Variable("x", 4), Variable("y", 4), Variable("z", 4))  # doctest: +NORMALIZE_WHITESPACE
        (0x3 & ((x - (0x5 & (x >> 0x1))) >> 0x2)) + (0x3 & (x - (0x5 & (x >> 0x1)))) +
        (0x3 & ((y - (0x5 & (y >> 0x1))) >> 0x2)) + (0x3 & (y - (0x5 & (y >> 0x1)))) +
        (0x3 & ((z - (0x5 & (z >> 0x1))) >> 0x2)) + (0x3 & (z - (0x5 & (z >> 0x1))))
        >>> PopCountSum3(Variable("x", 3), Variable("y", 3), Constant(0, 3))  # doctest: +NORMALIZE_WHITESPACE
        (0b00 :: ((0b0 :: (x[0])) + (0b0 :: (x[1])) + (0b0 :: (x[2])))) +
        (0b00 :: ((0b0 :: (y[0])) + (0b0 :: (y[1])) + (0b0 :: (y[2]))))

    """

    arity = [3, 0]
    is_symmetric = True
    is_simple = True

    @classmethod
    def condition(cls, x, y, z):
        return x.width == y.width == z.width

    @classmethod
    def output_width(cls, x, y, z):
        if x.width < 4:
            offset = 1
            if x.width == 3:
                offset = 2
            return PopCount.output_width(x) + offset
        else:
            return (x.width + y.width + z.width).bit_length()

    @classmethod
    def eval(cls, x, y, z):
        # Source: Hacker's Delight

        if x.width < 4:
            # the HW of a 1-bit/2-bit vector requires 1-bit/2-bit (HW(0b1)=0b1, HW(0b11)=0b10)
            # thus, the sum of three HW of these sizes require an extra bit (3*0b1=0b11, 3*0b10=0b110)
            # the HW of a 3-bit vector requires 2-bit (Hw(0b111)=0b11)
            # but the sum of three HW of 3-bit require two extra bit (3*0b11 = 0b1001)
            offset = 1
            if x.width == 3:
                offset = 2
            x = operation.ZeroExtend(PopCount(x), offset)
            y = operation.ZeroExtend(PopCount(y), offset)
            z = operation.ZeroExtend(PopCount(z), offset)
            return x + y + z
        elif x.width > 32:
            width = cls.output_width(x, y, z)
            x = PopCount(x)
            x = operation.ZeroExtend(x, width - x.width)
            y = PopCount(y)
            y = operation.ZeroExtend(y, width - y.width)
            z = PopCount(z)
            z = operation.ZeroExtend(z, width - z.width)
            return x + y + z

        orig_x, orig_y, orig_z = x, y, z
        while (x.width & (x.width - 1)) != 0:
            x = operation.ZeroExtend(x, 1)
        while (y.width & (y.width - 1)) != 0:
            y = operation.ZeroExtend(y, 1)
        while (z.width & (z.width - 1)) != 0:
            z = operation.ZeroExtend(z, 1)
        width_log2 = x.width.bit_length() - 1

        m_ctes = []
        for i in range(width_log2):
            m_ctes.append(repeat_pattern(pattern01(2 ** i), x.width))

        bv = core.Constant(0, x.width)
        for i, m in enumerate(m_ctes):
            if i == 0:
                x = x - ((x >> core.Constant(1, bv.width)) & m)
                y = y - ((y >> core.Constant(1, bv.width)) & m)
                z = z - ((z >> core.Constant(1, bv.width)) & m)
                bv = x + y + z
            elif i == 1:
                x = (x & m) + ((x >> core.Constant(2 ** i, bv.width)) & m)  # generic case
                y = (y & m) + ((y >> core.Constant(2 ** i, bv.width)) & m)
                z = (z & m) + ((z >> core.Constant(2 ** i, bv.width)) & m)
                bv = x + y + z
            elif i == 2:
                bv = (bv & m) + ((bv >> core.Constant(4, bv.width)) & m)
            elif i == 3:
                bv = bv + (bv >> core.Constant(8, bv.width))
            elif i == 4:
                bv = bv + (bv >> core.Constant(16, bv.width))

        return bv[cls.output_width(orig_x, orig_y, orig_z) - 1:]
arxpy/bitvector/operation.py
class BvUge(Operation):
    """Unsigned greater than or equal operator.

    It overrides >= and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvUgt
        >>> BvUge(Constant(2, 8), Constant(2, 8))
        0b1
        >>> BvUge(Constant(2, 8), 2)
        0b1
        >>> Constant(2, 8) >= 2
        0b1
        >>> Constant(2, 8) >= Variable("y", 8)
        0x02 >= y

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True
    infix_symbol = ">="

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return 1

    @classmethod
    def eval(cls, x, y):
        zero = core.Constant(0, 1)
        one = core.Constant(1, 1)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return one if x.val >= y.val else zero
arxpy/bitvector/operation.py
class BvNeg(Operation):
    """Unary minus operation.

    It overrides the unary operator -. See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvNeg
        >>> BvNeg(Constant(1, 8))
        0xff
        >>> -Constant(1, 8)
        0xff
        >>> BvNeg(Variable("x", 8))
        -x

    """

    arity = [1, 0]
    is_symmetric = False
    unary_symbol = "-"

    @classmethod
    def output_width(cls, x):
        return x.width

    @classmethod
    def eval(cls, x):
        def doit(x, width):
            """Unary minus operation when the operand is int."""
            return ((2 ** width) - x) % (2 ** width)

        if isinstance(x, core.Constant):
            return core.Constant(doit(int(x), x.width), x.width)
        elif isinstance(x, BvNeg):
            return x.args[0]
        # # disabled (all op equal precedence)
        # elif isinstance(x, BvAdd):
        #     return BvAdd(BvNeg(x.args[0]), BvNeg(x.args[1]))
        # elif isinstance(x, (BvMul, BvDiv, BvMod)):
        #     return x.func(BvNeg(x.args[0]), x.args[1])
arxpy/bitvector/operation.py
class ZeroExtend(Operation):
    """Extend with zeroes preserving the unsigned value.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import ZeroExtend
        >>> ZeroExtend(Constant(0x12, 8), 4)
        0x012
        >>> ZeroExtend(Variable("x", 8), 4)
        0x0 :: x

    """

    arity = [1, 1]
    is_symmetric = False
    alt_name = "ext"
    operand_types = [core.Term, int]

    @classmethod
    def condition(cls, x, i):
        return i >= 0

    @classmethod
    def output_width(cls, x, i):
        return x.width + i

    @classmethod
    def eval(cls, x, i):
        if i == 0:
            return x
        else:
            return Concat(core.Constant(0, i), x)
arxpy/bitvector/operation.py
class BvAnd(Operation):
    """Bitwise AND (logical conjunction) operation.

    It overrides the operator & and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvAnd
        >>> BvAnd(Constant(5, 8), Constant(3, 8))
        0x01
        >>> BvAnd(Constant(5, 8), 3)
        0x01
        >>> Constant(5, 8) & 3
        0x01
        >>> Variable("x", 8) & Variable("y", 8)
        x & y

    """

    arity = [2, 0]
    is_symmetric = True
    is_simple = True
    infix_symbol = "&"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y):
            """AND operation when both operands are int."""
            return x & y

        zero = core.Constant(0, x.width)
        allones = BvNot(zero)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y)), x.width)
        elif x == zero or y == zero:
            return zero
        elif x == allones:
            return y
        elif y == allones:
            return x
        elif x == y:
            return x
        elif x == BvNot(y):
            return zero

    def _simplify(self, *args, **kwargs):
        compatible_terms = [
            lambda x: x,
            lambda x: BvNot(x)
        ]

        return self._binary_symmetric_simplification(compatible_terms)
arxpy/bitvector/operation.py
class BvUgt(Operation):
    """Unsigned greater than operator.

    It overrides > and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvUgt
        >>> BvUgt(Constant(1, 8), Constant(2, 8))
        0b0
        >>> BvUgt(Constant(1, 8), 2)
        0b0
        >>> Constant(1, 8) > 2
        0b0
        >>> Constant(1, 8) > Variable("y", 8)
        0x01 > y

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True
    infix_symbol = ">"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return 1

    @classmethod
    def eval(cls, x, y):
        zero = core.Constant(0, 1)
        one = core.Constant(1, 1)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return one if x.val > y.val else zero
arxpy/bitvector/operation.py
class Concat(Operation):
    """Concatenation operation.

    Given the bit-vectors :math:`(x_{n-1}, \dots, x_0)` and
    :math:`(y_{m-1}, \dots, y_0)`, ``Concat(x, y)`` returns the bit-vector
    :math:`(x_{n-1}, \dots, x_0, y_{m-1}, \dots, y_0)`.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import Concat
        >>> Concat(Constant(0x12, 8), Constant(0x345, 12))
        0x12345
        >>> Concat(Variable("x", 8), Variable("y", 8))
        x :: y

    """

    arity = [2, 0]
    is_symmetric = False
    infix_symbol = "::"

    @classmethod
    def output_width(cls, x, y):
        return x.width + y.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y):
            """Concatenation when both operands are int."""
            return int(x.bin() + y.bin()[2:], 2)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(x, y), cls.output_width(x, y))
        elif isinstance(x, core.Constant) and isinstance(y, Concat) and \
                isinstance(y.args[0], core.Constant):
            return Concat(Concat(x, y.args[0]), y.args[1])
        elif isinstance(y, core.Constant) and isinstance(x, Concat) and \
                isinstance(x.args[1], core.Constant):
            return Concat(x.args[0], Concat(x.args[1], y))
        elif isinstance(x, Extract) and isinstance(y, Extract):
            # x[5:4] concat x[3:2] = x[5:2]
            if x.args[0] == y.args[0] and x.args[2] == y.args[1] + 1:
                return Extract(x.args[0], x.args[1], y.args[2])
arxpy/bitvector/core.py
class Variable(basic.Atom, Term):
    """Represent bit-vector variables.

    Args:
        name: the name of the variable.
        width: the bit-width.

    ::

        >>> from arxpy.bitvector.core import Variable
        >>> Variable("x", 12)
        x
        >>> Variable("x", 12).vrepr()
        "Variable('x', width=12)"

    """

    def _hashable_content(self):
        """Return a tuple of information about self to compute hash."""
        return self.name, self.width

    # def __call__(self, *args):
    #     from sympy.core.function as function
    #     return function.UndefinedFunction(self.name, self.width)(*args)

    # end Symbol

    __slots__ = ['_name']

    def __new__(cls, name, width):
        assert isinstance(name, str)
        obj = Term.__new__(cls, width=width)
        obj._name = name

        return obj

    @property
    def name(self):
        """The name of the variable."""
        return self._name

    @property
    def formula_size(self):
        """The formula size of the variable."""
        def log2(n):
            return int(math.ceil(math.log(n, 2)))

        def bin_enc(n):
            return 1 + log2(n + 1)

        return 1 + bin_enc(self.width)
arxpy/bitvector/core.py
class Constant(basic.Atom, Term):
    """Represent bit-vector constants.

    Bit-vector constants are interpreted as unsigned integers in base 2,
    that is, a bit-vector :math:`(x_{n-1}, \dots, x_1, x_0)` represents
    the non-negative integer :math:`x_0 + 2 x_1 + \dots + 2^{n-1} x_{n-1}`.

    Args:
        val: the integer value.
        width: the bit-width.

    ::

        >>> from arxpy.bitvector.core import Constant
        >>> Constant(3, 12)
        0x003
        >>> Constant(0b11, 12)
        0x003
        >>> Constant(0x003, 12)
        0x003
        >>> Constant(3, 12).vrepr()
        'Constant(0b000000000011, width=12)'

    """

    def __int__(self):
        return self.val

    def __hash__(self):
        return super().__hash__()

    def __eq__(self, other):
        """Override == operator."""
        if isinstance(other, int):
            return self.val == other
        elif isinstance(other, Constant) and self.width == other.width:
            return self.val == other.val
        else:
            return False

    # def __index__(self):
    #     """Return an int to be used inside a slice [ : : ]."""
    #     return self.int

    def _hashable_content(self):
        """Return a tuple of information about self to compute its hash."""
        return self.val, self.width

    @classmethod
    def class_key(cls):
        """Return the key (identifier) of the class for sorting."""
        return 1, 0, cls.__name__

    # end Integer

    __slots__ = ["_val"]

    def __new__(cls, val, width):
        assert isinstance(val, int) and 0 <= val < 2 ** width
        obj = Term.__new__(cls, width=width)
        obj._val = val
        return obj

    def __bool__(self):
        if self.width == 1:
            return self == Constant(1, 1)
        else:
            raise AttributeError("only 1-bit constants implement bool()")

    @property
    def val(self):
        """The integer represented by the bit-vector constant."""
        return self._val

    @property
    def formula_size(self):
        """The formula size of the constant."""
        def log2(n):
            return int(math.ceil(math.log(n, 2)))

        def bin_enc(n):
            return 1 + log2(n + 1)

        return 1 + log2(int(self) + 1) + bin_enc(self.width)

    def bin(self):
        """Return the binary representation.

            >>> from arxpy.bitvector.core import Constant
            >>> print(Constant(3, 4).bin())
            0b0011
            >>> print(Constant(4, 6).bin())
            0b000100

        """
        width = self.width + 2  # 2 due to '0b'
        return format(self.val, r'0=#{}b'.format(width))

    def hex(self):
        """Return the hexadecimal representation.

            >>> from arxpy.bitvector.core import Constant
            >>> print(Constant(3, 4).hex())
            0x3

        """
        assert self.width % 4 == 0
        width = (self.width // 4) + 2
        return format(self.val, '0=#{}x'.format(width))

    def oct(self):
        """Return the octal representation.

            >>> from arxpy.bitvector.core import Constant
            >>> print(Constant(4, 6).oct())
            0o04

        """
        assert self.width % 3 == 0
        width = (self.width // 3) + 2
        return format(self.val, '0=#{}o'.format(width))
arxpy/bitvector/operation.py
class BvSub(Operation):
    """Modular subtraction operation.

    It overrides the operator - and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvSub
        >>> BvSub(Constant(1, 8), Constant(2, 8))
        0xff
        >>> BvSub(Constant(1, 8), 2)
        0xff
        >>> Constant(1, 8) - 2
        0xff
        >>> Variable("x", 8) - Variable("y", 8)
        x - y

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True
    infix_symbol = "-"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y, width):
            """Modular subtraction when both operands are integers."""
            return (x - y) % (2 ** width)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y), x.width), x.width)

        zero = core.Constant(0, x.width)

        if x == zero:
            return BvNeg(y)
        elif y == zero:
            return x
        elif x == y:
            return zero
        elif isinstance(x, BvAdd):  # (x0 + x1) - y
            if x.args[0] == y:
                return x.args[1]
            elif x.args[1] == y:
                return x.args[0]
        elif isinstance(y, BvAdd):  # x - (y0 + y1)
            if y.args[0] == x:
                return BvNeg(y.args[1])
            elif y.args[1] == x:
                return BvNeg(y.args[0])
arxpy/bitvector/extraop.py
class PopCountDiff(operation.Operation):
    """Compute the difference of the hamming weight of two words

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.extraop import PopCountDiff
        >>> PopCountDiff(Constant(0b011, 3), Constant(0b100, 3))
        0b01
        >>> PopCountDiff(Variable("x", 4), Variable("y", 4))  # doctest: +NORMALIZE_WHITESPACE
        (((0x3 & ((x - (0x5 & (x >> 0x1))) >> 0x2)) + (0x3 & (x - (0x5 & (x >> 0x1)))) +
        (0x3 & ((~y - (0x5 & (~y >> 0x1))) >> 0x2)) + (0x3 & (~y - (0x5 & (~y >> 0x1))))) - 0x4)[2:]
        >>> PopCountDiff(Variable("x", 3), Variable("y", 3))  # doctest: +NORMALIZE_WHITESPACE
        ((0b0 :: (x[0])) + (0b0 :: (x[1])) + (0b0 :: (x[2]))) -
        ((0b0 :: (y[0])) + (0b0 :: (y[1])) + (0b0 :: (y[2])))

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width.bit_length()

    @classmethod
    def eval(cls, x, y):
        # Source: Hacker's Delight

        if x.width < 4:
            return PopCount(x) - PopCount(y)
        elif x.width > 32:
            return PopCount(x) - PopCount(y)

        orig_x, orig_y = x, y
        while (x.width & (x.width - 1)) != 0:
            x = operation.ZeroExtend(x, 1)
        while (y.width & (y.width - 1)) != 0:
            y = operation.ZeroExtend(y, 1)
        width_log2 = x.width.bit_length() - 1

        m_ctes = []
        for i in range(width_log2):
            m_ctes.append(repeat_pattern(pattern01(2 ** i), x.width))

        bv = core.Constant(0, x.width)
        for i, m in enumerate(m_ctes):
            if i == 0:
                x = x - ((x >> core.Constant(1, bv.width)) & m)
                y = (~y) - (((~y) >> core.Constant(1, bv.width)) & m)
                bv = x + y
            elif i == 1:
                x = (x & m) + ((x >> core.Constant(2 ** i, bv.width)) & m)  # generic case
                y = (y & m) + ((y >> core.Constant(2 ** i, bv.width)) & m)
                bv = x + y
            elif i == 2:
                bv = (bv & m) + ((bv >> core.Constant(4, bv.width)) & m)
            elif i == 3:
                bv = bv + (bv >> core.Constant(8, bv.width))
            elif i == 4:
                bv = bv + (bv >> core.Constant(16, bv.width))

        return (bv - y.width)[cls.output_width(orig_x, orig_y) - 1:]
arxpy/bitvector/operation.py
class Repeat(Operation):
    """Concatenate a bit-vector with itself a given number of times.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import Repeat
        >>> Repeat(Constant(0x1, 4), 4)
        0x1111
        >>> Repeat(Variable("x", 8), 4)
        x :: x :: x :: x

    """

    arity = [1, 1]
    is_symmetric = False
    operand_types = [core.Term, int]

    @classmethod
    def condition(cls, x, i):
        return i >= 1

    @classmethod
    def output_width(cls, x, i):
        return i * x.width

    @classmethod
    def eval(cls, x, i):
        if i == 1:
            return x
        else:
            # noinspection PyTypeChecker
            return functools.reduce(Concat, itertools.repeat(x, i))
arxpy/bitvector/operation.py
class Ite(Operation):
    """If-then-else operator.

    ``Ite(b, x, y)`` returns ``x`` if ``b == 0b1`` and ``y`` otherwise.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import Ite
        >>> Ite(Constant(0, 1), Constant(0b11, 2), Constant(0b00, 2))
        0b00
        >>> Ite(Constant(1, 1), Constant(0x1, 4), Constant(0x0, 4))
        0x1

    """

    arity = [3, 0]
    is_symmetric = False

    @classmethod
    def condition(cls, b, x, y):
        return b.width == 1 and x.width == y.width

    @classmethod
    def output_width(cls, b, x, y):
        return x.width

    @classmethod
    def eval(cls, b, x, y):
        if b == core.Constant(1, 1):
            return x
        elif b == core.Constant(0, 1):
            return y
arxpy/bitvector/extraop.py
class PopCountSum2(operation.Operation):
    """Count the number of 1's of two bit-vectors.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.extraop import PopCountSum2
        >>> PopCountSum2(Constant(0b000, 3), Constant(0b001, 3))
        0b001
        >>> PopCountSum2(~Constant(0, 15), ~Constant(0, 15))
        0b11110
        >>> PopCountSum2(Variable("x", 4), Variable("y", 4))  # doctest: +NORMALIZE_WHITESPACE
        (0x3 & ((x - (0x5 & (x >> 0x1))) >> 0x2)) + (0x3 & (x - (0x5 & (x >> 0x1)))) +
        (0x3 & ((y - (0x5 & (y >> 0x1))) >> 0x2)) + (0x3 & (y - (0x5 & (y >> 0x1))))
        >>> PopCountSum2(Variable("x", 3), Variable("y", 3))  # doctest: +NORMALIZE_WHITESPACE
        (0b0 :: ((0b0 :: (x[0])) + (0b0 :: (x[1])) + (0b0 :: (x[2])))) +
        (0b0 :: ((0b0 :: (y[0])) + (0b0 :: (y[1])) + (0b0 :: (y[2]))))

    """

    arity = [2, 0]
    is_symmetric = True
    is_simple = True

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        if x.width < 4:
            return PopCount.output_width(x) + 1
        else:
            return (x.width + y.width).bit_length()

    @classmethod
    def eval(cls, x, y):
        # Source: Hacker's Delight

        if x.width < 4:
            # the HW of a 1-bit/2-bit vector requires 1-bit/2-bit (HW(0b1)=0b1, HW(0b11)=0b10)
            # thus, the sum of two HW of these sizes require an extra bit
            # the HW of a 3-bit vector requires 2-bit (Hw(0b111)=0b11)
            # and the sum of two HW of 3-bit also require an extra bit
            return operation.ZeroExtend(PopCount(x), 1) + operation.ZeroExtend(PopCount(y), 1)
        elif x.width > 32:
            width = cls.output_width(x, y)
            x = PopCount(x)
            x = operation.ZeroExtend(x, width - x.width)
            y = PopCount(y)
            y = operation.ZeroExtend(y, width - y.width)
            return x + y

        orig_x, orig_y = x, y
        while (x.width & (x.width - 1)) != 0:
            x = operation.ZeroExtend(x, 1)
        while (y.width & (y.width - 1)) != 0:
            y = operation.ZeroExtend(y, 1)
        width_log2 = x.width.bit_length() - 1

        m_ctes = []
        for i in range(width_log2):
            m_ctes.append(repeat_pattern(pattern01(2 ** i), x.width))

        bv = core.Constant(0, x.width)
        for i, m in enumerate(m_ctes):
            if i == 0:
                x = x - ((x >> core.Constant(1, bv.width)) & m)
                y = y - ((y >> core.Constant(1, bv.width)) & m)
                bv = x + y
            elif i == 1:
                x = (x & m) + ((x >> core.Constant(2 ** i, bv.width)) & m)  # generic case
                y = (y & m) + ((y >> core.Constant(2 ** i, bv.width)) & m)
                bv = x + y
            elif i == 2:
                bv = (bv & m) + ((bv >> core.Constant(4, bv.width)) & m)
            elif i == 3:
                bv = bv + (bv >> core.Constant(8, bv.width))
            elif i == 4:
                bv = bv + (bv >> core.Constant(16, bv.width))

        return bv[cls.output_width(orig_x, orig_y) - 1:]
arxpy/bitvector/operation.py
class RotateLeft(Operation):
    """Circular left rotation operation.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import RotateLeft
        >>> RotateLeft(Constant(150, 8), 2)
        0x5a
        >>> RotateLeft(Variable("x", 8), 2)
        x <<< 2

    """

    arity = [1, 1]
    is_symmetric = False
    infix_symbol = "<<<"
    operand_types = [core.Term, int]

    @classmethod
    def condition(cls, x, r):
        return x.width > r >= 0

    @classmethod
    def output_width(cls, x, r):
        return x.width

    @classmethod
    def eval(cls, x, r):
        def doit(val, r, width):
            """Left cyclic rotation operation when both operands are int."""
            mask = 2 ** width - 1
            r = r % width
            return ((val << r) & mask) | ((val & mask) >> (width - r))

        if isinstance(x, core.Constant):
            return core.Constant(doit(int(x), r, x.width), x.width)
        elif r == 0:
            return x
        elif isinstance(x, RotateLeft):
            return RotateLeft(x.args[0], (x.args[1] + r) % x.args[0].width)
        elif isinstance(x, RotateRight):
            return RotateRight(x.args[0], (x.args[1] - r) % x.args[0].width)
arxpy/bitvector/operation.py
class RotateRight(Operation):
    """Circular right rotation operation.

    It provides Automatic Constant Conversion. See `Operation` for more
    information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import RotateRight
        >>> RotateRight(Constant(150, 8), 3)
        0xd2
        >>> RotateRight(Variable("x", 8), 3)
        x >>> 3

    """

    arity = [1, 1]
    is_symmetric = False
    infix_symbol = ">>>"
    operand_types = [core.Term, int]

    @classmethod
    def condition(cls, x, r):
        return x.width > r >= 0

    @classmethod
    def output_width(cls, x, r):
        return x.width

    @classmethod
    def eval(cls, x, r):
        def doit(val, r, width):
            """Right cyclic rotation operation when both operands are int."""
            mask = 2 ** width - 1
            r = r % width
            return ((val & mask) >> r) | (val << (width - r) & mask)

        if isinstance(x, core.Constant):
            return core.Constant(doit(int(x), r, x.width), x.width)
        elif r == 0:
            return x
        elif isinstance(x, RotateRight):
            return RotateRight(x.args[0], (x.args[1] + r) % x.args[0].width)
        elif isinstance(x, RotateLeft):
            return RotateLeft(x.args[0], (x.args[1] - r) % x.args[0].width)
arxpy/bitvector/operation.py
class BvXor(Operation):
    """Bitwise XOR (exclusive-or) operation.

    It overrides the operator ^ and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvXor
        >>> BvXor(Constant(5, 8), Constant(3, 8))
        0x06
        >>> BvXor(Constant(5, 8), 3)
        0x06
        >>> Constant(5, 8) ^ 3
        0x06
        >>> Variable("x", 8) ^ Variable("y", 8)
        x ^ y

    """

    arity = [2, 0]
    is_symmetric = True
    is_simple = True
    infix_symbol = "^"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y):
            """XOR operation when both operands are int."""
            return x ^ y

        zero = core.Constant(0, x.width)
        allones = BvNot(zero)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y)), x.width)
        elif x == zero:
            return y
        elif y == zero:
            return x
        elif x == allones:
            return BvNot(y)
        elif y == allones:
            return BvNot(x)
        elif x == y:
            return zero
        elif x == BvNot(y):
            return allones

    def _simplify(self, *args, **kwargs):
        compatible_terms = [
            lambda x: x,
            lambda x: BvNot(x)
        ]

        return self._binary_symmetric_simplification(compatible_terms)
arxpy/bitvector/operation.py
class BvUlt(Operation):
    """Unsigned less than operator.

    It overrides < and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvUlt
        >>> BvUlt(Constant(1, 8), Constant(2, 8))
        0b1
        >>> BvUlt(Constant(1, 8), 2)
        0b1
        >>> Constant(1, 8) < 2
        0b1
        >>> Constant(1, 8) < Variable("y", 8)
        0x01 < y

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True
    infix_symbol = "<"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return 1

    @classmethod
    def eval(cls, x, y):
        zero = core.Constant(0, 1)
        one = core.Constant(1, 1)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return one if x.val < y.val else zero
arxpy/bitvector/extraop.py
class LeadingZeros(operation.Operation):
    """Return a bit-vector with the leading zeros set to one and the rest to zero.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.extraop import PopCount
        >>> LeadingZeros(Constant(0b11111, 5))
        0b00000
        >>> LeadingZeros(Constant(0b001, 3))
        0b110
        >>> LeadingZeros(Variable("x", 4))  # doctest: +NORMALIZE_WHITESPACE
        ~(((x | (x >> 0x1)) >> 0x2) | x | (x >> 0x1))
        >>> LeadingZeros(Variable("x", 3))  # doctest: +NORMALIZE_WHITESPACE
        ~((((((0b0 :: x) >> 0x1) | (0b0 :: x)) >> 0x2) | ((0b0 :: x) >> 0x1) | (0b0 :: x))[2:])

    """

    arity = [1, 0]
    is_symmetric = False

    @classmethod
    def output_width(cls, x):
        return x.width

    @classmethod
    def eval(cls, bv):
        # Source: Hacker's Delight

        if bv.width == 1:
            return ~bv

        original_width = bv.width
        while (bv.width & (bv.width - 1)) != 0:
            bv = operation.ZeroExtend(bv, 1)
        width_log2 = bv.width.bit_length() - 1

        for i in range(width_log2):
            bv = bv | (bv >> core.Constant(2 ** i, bv.width))
        return ~bv[original_width - 1:]
arxpy/bitvector/extraop.py
class Reverse(operation.Operation):
    """Reverse the bit-vector.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.extraop import PopCount
        >>> Reverse(Constant(0b1010, 4))
        0x5
        >>> Reverse(Constant(0b001, 3))
        0b100
        >>> Reverse(Variable("x", 4))  # doctest: +NORMALIZE_WHITESPACE
        (0x3 & (((0x5 & (x >> 0x1)) | ((0x5 & x) << 0x1)) >> 0x2)) |
        ((0x3 & ((0x5 & (x >> 0x1)) | ((0x5 & x) << 0x1))) << 0x2)
        >>> Reverse(Variable("x", 3))  # doctest: +NORMALIZE_WHITESPACE
        (x[0]) :: (x[1]) :: (x[2])

    """

    arity = [1, 0]
    is_symmetric = False

    @classmethod
    def output_width(cls, x):
        return x.width

    @classmethod
    def eval(cls, bv):
        # Source: Hacker's Delight

        if bv.width == 1:
            return bv
        elif bv.width == 2:
            return operation.RotateLeft(bv, 1)
        elif bv.width == 3:
            return operation.Concat(operation.Concat(bv[0], bv[1]), bv[2])

        original_width = bv.width
        while (bv.width & (bv.width - 1)) != 0:
            bv = operation.ZeroExtend(bv, 1)
        width_log2 = bv.width.bit_length() - 1

        m_ctes = []
        for i in range(width_log2):
            m_ctes.append(repeat_pattern(pattern01(2 ** i), bv.width))

        if bv.width > 32:
            for i, m in list(enumerate(m_ctes)):
                bv = ((bv & m) << core.Constant(2 ** i, bv.width)) | ((bv >> core.Constant(2 ** i, bv.width)) & m)
            return bv[:bv.width - original_width]

        for i, m in list(enumerate(m_ctes))[:3]:
            bv = ((bv & m) << core.Constant(2 ** i, bv.width)) | ((bv >> core.Constant(2 ** i, bv.width)) & m)  # generic case

        if len(m_ctes) == 4:
            bv = ((bv & m_ctes[3]) << core.Constant(8, bv.width)) | ((bv >> core.Constant(8, bv.width)) & m_ctes[3])
        elif len(m_ctes) == 5:
            rol = operation.RotateLeft
            ror = operation.RotateRight
            bv = ror(bv & m_ctes[3], 8) | (rol(bv, 8) & m_ctes[3])

        return bv[:bv.width - original_width]
arxpy/bitvector/extraop.py
class PopCount(operation.Operation):
    """Count the number of 1's in the bit-vector.

    This operation is also known as the hamming weight of a bit-vector.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.extraop import PopCount
        >>> PopCount(Constant(0b1010, 4))
        0b010
        >>> PopCount(Constant(0b101, 3))
        0b10
        >>> PopCount(Variable("x", 4))  # doctest: +NORMALIZE_WHITESPACE
        ((0x3 & ((x - (0x5 & (x >> 0x1))) >> 0x2)) + (0x3 & (x - (0x5 & (x >> 0x1)))))[2:]
        >>> PopCount(Variable("x", 3))  # doctest: +NORMALIZE_WHITESPACE
        (0b0 :: (x[0])) + (0b0 :: (x[1])) + (0b0 :: (x[2]))

    """

    arity = [1, 0]
    is_symmetric = False

    @classmethod
    def output_width(cls, x):
        return x.width.bit_length()

    @classmethod
    def eval(cls, bv):
        # Source: Hacker's Delight

        if bv.width < 4:
            w = cls.output_width(bv)
            return sum([operation.ZeroExtend(bv[i], w-1) for i in range(bv.width)])

        # extend the bv until power of 2 length
        original_width = bv.width
        while (bv.width & (bv.width - 1)) != 0:
            bv = operation.ZeroExtend(bv, 1)
        width_log2 = bv.width.bit_length() - 1

        m_ctes = []
        for i in range(width_log2):
            m_ctes.append(repeat_pattern(pattern01(2 ** i), bv.width))

        if bv.width > 32:
            for i, m in enumerate(m_ctes):
                bv = (bv & m) + ((bv >> core.Constant(2 ** i, bv.width)) & m)
            return bv[original_width.bit_length() - 1:]

        for i, m in enumerate(m_ctes):
            if i == 0:
                bv = bv - ((bv >> core.Constant(1, bv.width)) & m)
            elif i == 1:
                bv = (bv & m) + ((bv >> core.Constant(2 ** i, bv.width)) & m)  # generic case
            elif i == 2:
                bv = (bv + (bv >> core.Constant(4, bv.width))) & m
            elif i == 3:
                bv = bv + (bv >> core.Constant(8, bv.width))
            elif i == 4:
                bv = bv + (bv >> core.Constant(16, bv.width))

        return bv[original_width.bit_length() - 1:]
arxpy/bitvector/operation.py
class BvNot(Operation):
    """Bitwise negation operation.

    It overrides the operator ~. See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvNot
        >>> BvNot(Constant(0b1010101, 7))
        0b0101010
        >>> ~Constant(0b1010101, 7)
        0b0101010
        >>> ~Variable("x", 8)
        ~x

    """

    arity = [1, 0]
    is_symmetric = False
    unary_symbol = "~"

    @classmethod
    def output_width(cls, x):
        return x.width

    @classmethod
    def eval(cls, x):
        def doit(x, width):
            """NOT operation when the operand is int."""
            return ~x % (2 ** width)

        if isinstance(x, core.Constant):
            return core.Constant(doit(int(x), x.width), x.width)
        elif isinstance(x, BvNot):
            return x.args[0]
        # # De Morgan's laws (disabled, all op equal precedence)
        # if isinstance(x, BvAnd):
        #     return BvOr(BvNot(x.args[0]), BvNot(x.args[1]))
        # elif isinstance(x, BvOr):
        #     return BvAnd(BvNot(x.args[0]), BvNot(x.args[1]))
arxpy/bitvector/operation.py
class BvMul(Operation):
    """Modular multiplication operation.

    It overrides the operator * and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvMul
        >>> BvMul(Constant(4, 8), Constant(3, 8))
        0x0c
        >>> BvMul(Constant(4, 8), 3)
        0x0c
        >>> Constant(4, 8) * 3
        0x0c
        >>> Variable("x", 8) * Variable("y", 8)
        x * y

    """

    arity = [2, 0]
    is_symmetric = True
    is_simple = True
    infix_symbol = "*"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y, width):
            """Modular multiplication when both operands are int."""
            return (x * y) % (2 ** width)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y), x.width), x.width)

        zero = core.Constant(0, x.width)
        one = core.Constant(1, x.width)

        if x == zero or y == zero:
            return zero
        elif x == one:
            return y
        elif y == one:
            return x
arxpy/bitvector/operation.py
class BvUle(Operation):
    """Unsigned less than or equal operator.

    It overrides <= and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvUle
        >>> BvUle(Constant(2, 8), Constant(2, 8))
        0b1
        >>> BvUle(Constant(2, 8), 2)
        0b1
        >>> Constant(2, 8) <= 2
        0b1
        >>> Constant(2, 8) <= Variable("y", 8)
        0x02 <= y

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True
    infix_symbol = "<="

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return 1

    @classmethod
    def eval(cls, x, y):
        zero = core.Constant(0, 1)
        one = core.Constant(1, 1)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return one if x.val <= y.val else zero
arxpy/bitvector/operation.py
class BvUdiv(Operation):
    """Unsigned and truncated division operation.

    It overrides the operator / and provides Automatic Constant Conversion.
    See `Operation` for more information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvUdiv
        >>> BvUdiv(Constant(0x0c, 8), Constant(3, 8))
        0x04
        >>> BvUdiv(Constant(0x0c, 8), 3)
        0x04
        >>> Constant(0x0c, 8) / 3
        0x04
        >>> Variable("x", 8) / Variable("y", 8)
        x / y

    """

    arity = [2, 0]
    is_symmetric = False
    is_simple = True
    infix_symbol = "/"

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return x.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y):
            """Division operation (truncated) when both operands are int."""
            assert y != 0
            return x // y

        zero = core.Constant(0, x.width)
        one = core.Constant(1, x.width)

        assert y != zero

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(int(x), int(y)), x.width)
        elif x == y:
            return one
        elif x == zero:
            return zero
        elif y == one:
            return x


Question: import doctest
import unittest
    import arxpy.bitvector.operation
    import arxpy.bitvector.extraop
from hypothesis import given, settings
from hypothesis.strategies import integers
from arxpy.bitvector.core import Constant, Variable, bitvectify
from arxpy.bitvector.extraop import PopCount, Reverse, PopCountSum2, PopCountSum3, PopCountDiff, LeadingZeros
from arxpy.bitvector.operation import (
    BvAnd, BvOr, BvXor, BvComp, BvUlt, BvUle, BvUgt, BvUge, BvShl,
    BvLshr, RotateLeft, RotateRight, Concat, BvAdd, BvSub, BvMul, BvUdiv,
    BvUrem, BvNeg, BvNot, Extract, ZeroExtend, Repeat, Ite
)
            from pysmt import shortcuts as sc
"""Tests for the operation module."""



MIN_SIZE = 2
MAX_SIZE = 32


simple_op = {
    BvAnd, BvOr, BvXor, BvComp, BvUlt, BvUle, BvUgt, BvUge, BvShl,
    BvLshr, BvAdd, BvSub, BvMul, BvUdiv, BvUrem}

Answer: unary_op = {BvNeg, BvNot}