Unary operation: SignExtend

        :param new_length: New length after sign-extension
        :return: A new StridedInterval

def sign_extend(self, new_length):
        """
        Unary operation: SignExtend

        :param new_length: New length after sign-extension
        :return: A new StridedInterval
        """

        msb = self.extract(self.bits - 1, self.bits - 1).eval(2)
        if msb == [ 0 ]:
            # All positive numbers
            return self.zero_extend(new_length)
        if msb == [ 1 ]:
            # All negative numbers
            si = self.copy()
            si._bits = new_length
            mask = (2 ** new_length - 1) - (2 ** self.bits - 1)
            si._lower_bound |= mask
            si._upper_bound |= mask

        else:
            # Both positive numbers and negative numbers
            numbers = self._nsplit()
            # Since there are both positive and negative numbers, there must be two bounds after nsplit
            # assert len(numbers) == 2

            all_resulting_intervals = list()

            assert len(numbers) > 0

            for n in numbers:
                a, b = n.lower_bound, n.upper_bound
                mask_a = 0
                mask_b = 0
                mask_n = ((1 << (new_length - n.bits)) - 1) << n.bits

                if StridedInterval._get_msb(a, n.bits) == 1:
                    mask_a = mask_n
                if StridedInterval._get_msb(b, n.bits) == 1:
                    mask_b = mask_n

                si_ = StridedInterval(bits=new_length, stride=n.stride, lower_bound=a | mask_a, upper_bound=b | mask_b)
                all_resulting_intervals.append(si_)
            si = StridedInterval.least_upper_bound(*all_resulting_intervals).normalize()

        si.uninitialized = self.uninitialized
        return si