Binary operation: multiplication

        :param o: The other operand
        :return: self * o

def mul(self, o):
        """
        Binary operation: multiplication

        :param o: The other operand
        :return: self * o
        """
        if self.is_integer and o.is_integer:
            # Two integers!
            a, b = self.lower_bound, o.lower_bound
            ret = StridedInterval(bits=self.bits,
                                  stride=0,
                                  lower_bound=a * b,
                                  upper_bound=a * b
                                  )

            if a * b > (2 ** self.bits - 1):
                logger.warning('Overflow in multiplication detected.')

            return ret.normalize()

        else:
            # All other cases

            # Cut from both north pole and south pole
            si1_psplit = self._psplit()
            si2_psplit = o._psplit()
            all_resulting_intervals = list()

            for si1 in si1_psplit:
                for si2 in si2_psplit:
                    tmp_unsigned_mul = self._wrapped_unsigned_mul(si1, si2)
                    tmp_signed_mul = self._wrapped_signed_mul(si1, si2)
                    for tmp_meet in tmp_unsigned_mul._multi_valued_intersection(tmp_signed_mul):
                        all_resulting_intervals.append(tmp_meet)
        return StridedInterval.least_upper_bound(*all_resulting_intervals).normalize()