Perform wrapped signed multiplication on two StridedIntervals.

        :param a: The first operand (StridedInterval)
        :param b: The second operand (StridedInterval)
        :return: The product

def _wrapped_signed_mul(a, b):
        """
        Perform wrapped signed multiplication on two StridedIntervals.

        :param a: The first operand (StridedInterval)
        :param b: The second operand (StridedInterval)
        :return: The product
        """

        #NOTE: interval here should never straddle poles
        #FIXME: add assert to be sure of it!

        if a.bits != b.bits:
            logger.warning("Signed mul: two parameters have different bit length")

        bits = max(a.bits, b.bits)

        # shorter SI
        a_lb_positive = StridedInterval._is_msb_zero(a.lower_bound, bits)
        a_ub_positive = StridedInterval._is_msb_zero(a.upper_bound, bits)
        b_lb_positive = StridedInterval._is_msb_zero(b.lower_bound, bits)
        b_ub_positive = StridedInterval._is_msb_zero(b.upper_bound, bits)
        uninit_flag = a.uninitialized | b.uninitialized

        if b.is_integer:
            if b_lb_positive:
                stride = abs(a.stride * b.lower_bound)
            else:
                # if the number is negative we have to get its value first
                stride = abs(a.stride * StridedInterval._unsigned_to_signed(b.lower_bound, bits))
        elif a.is_integer:
            if a_lb_positive:
                stride = abs(b.stride * a.lower_bound)
            else:
                # if the number is negative we have to get its value first:
                stride = abs(b.stride * StridedInterval._unsigned_to_signed(a.lower_bound, bits))
        else:
            stride = fractions.gcd(a.stride, b.stride)

        if a_lb_positive and a_ub_positive and b_lb_positive and b_ub_positive:
            # [2, 5] * [10, 20] = [20, 100]
            lb = a.lower_bound * b.lower_bound
            ub = a.upper_bound * b.upper_bound

            if ub - lb < (2 ** bits):
                return StridedInterval(bits=bits, stride=stride, lower_bound=lb, upper_bound=ub, uninitialized=uninit_flag)
            else:
                return StridedInterval.top(bits, uninitialized=uninit_flag)

        elif not a_lb_positive and not a_ub_positive and not b_lb_positive and not b_ub_positive:
            # [-5, -2] * [-20, -10] = [20, 100]
            lb = (
                StridedInterval._unsigned_to_signed(a.upper_bound, bits) *
                StridedInterval._unsigned_to_signed(b.upper_bound, bits)
            )
            ub = (
                StridedInterval._unsigned_to_signed(a.lower_bound, bits) *
                StridedInterval._unsigned_to_signed(b.lower_bound, bits)
            )

            if ub - lb < (2 ** bits):
                return StridedInterval(bits=bits, stride=stride, lower_bound=lb, upper_bound=ub, uninitialized=uninit_flag)
            else:
                return StridedInterval.top(bits, uninitialized=uninit_flag)

        elif not a_lb_positive and not a_ub_positive and b_lb_positive and b_ub_positive:
            # [-10, -2] * [2, 5] = [-50, -4]
            lb = StridedInterval._unsigned_to_signed(a.lower_bound, bits) * b.upper_bound
            ub = StridedInterval._unsigned_to_signed(a.upper_bound, bits) * b.lower_bound
            # since the intervals do not straddle the poles, ub is greater than lb
            if ub - lb < (2 ** bits):
                lb &= (2 ** bits - 1)
                ub &= (2 ** bits - 1)
                return StridedInterval(bits=bits, stride=stride, lower_bound=lb, upper_bound=ub, uninitialized=uninit_flag)
            else:
                return StridedInterval.top(bits, uninitialized=uninit_flag)

        elif a_lb_positive and a_ub_positive and not b_lb_positive and not b_ub_positive:
            # [2, 10] * [-5, -2] = [-50, -4]
            lb = a.upper_bound * StridedInterval._unsigned_to_signed(b.lower_bound, bits)
            ub = a.lower_bound * StridedInterval._unsigned_to_signed(b.upper_bound, bits)
            # since the intervals do not straddle the poles, ub is greater than lb
            if ub - lb < (2 ** bits):
                lb &= (2 ** bits - 1)
                ub &= (2 ** bits - 1)
                return StridedInterval(bits=bits, stride=stride, lower_bound=lb, upper_bound=ub, uninitialized=uninit_flag)
            else:
                return StridedInterval.top(bits, uninitialized=uninit_flag)

        else:
            raise Exception('We shouldn\'t see this case: %s * %s' % (a, b))