def test_contained_union(self):
        intervals = Intervals(0, True, 10, True)
        self.assertEqual("[0, 10]", str(intervals))

        uniend = Intervals(0, False, 5, False)
        intervals.union(uniend)
        self.assertEqual("[0, 10]", str(intervals))
    def test_merge_multiple_union(self):
        intervals = Intervals(0, True, 10, True)
        self.assertEqual("[0, 10]", str(intervals))

        uniend = Intervals(15, False, 16, False)
        intervals.union(uniend)
        self.assertEqual("[0, 10] (15, 16)", str(intervals))

        uniend = Intervals(-15, False, 16, False)
        intervals.union(uniend)
        self.assertEqual("(-15, 16)", str(intervals))
    def test_right_extension_union(self):
        intervals = Intervals(0, True, 10, True)
        self.assertEqual("[0, 10]", str(intervals))

        uniend = Intervals(0, False, 15, False)
        intervals.union(uniend)
        self.assertEqual("[0, 15)", str(intervals))

        uniend = Intervals(15, False, 15, True)
        intervals.union(uniend)
        self.assertEqual("[0, 15]", str(intervals))
    def test_left_extension_union(self):
        intervals = Intervals(0, True, 10, True)
        self.assertEqual("[0, 10]", str(intervals))

        uniend = Intervals(-5, False, 0, False)
        intervals.union(uniend)
        self.assertEqual("(-5, 10]", str(intervals))

        uniend = Intervals(-5, True, 0, False)
        intervals.union(uniend)
        self.assertEqual("[-5, 10]", str(intervals))
    def test_boundary_union(self):
        intervals = Intervals(0, False, 10, True)
        self.assertEqual("(0, 10]", str(intervals))

        uniend = Intervals(10, False, 16, False)
        intervals.union(uniend)
        self.assertEqual("(0, 16)", str(intervals))

        uniend = Intervals(16, False, 20, False)
        intervals.union(uniend)
        self.assertEqual("(0, 16) (16, 20)", str(intervals))

        uniend = Intervals(-5, False, 0, False)
        intervals.union(uniend)
        self.assertEqual("(-5, 0) (0, 16) (16, 20)", str(intervals))
    def test_new_interval_union(self):
        intervals = Intervals(0, True, 10, True)
        self.assertEqual("[0, 10]", str(intervals))

        uniend = Intervals(15, False, 16, False)
        intervals.union(uniend)
        self.assertEqual("[0, 10] (15, 16)", str(intervals))

        uniend = Intervals(-5, False, -3, False)
        intervals.union(uniend)
        self.assertEqual("(-5, -3) [0, 10] (15, 16)", str(intervals))

        uniend = Intervals(13, True, 14, True)
        intervals.union(uniend)
        self.assertEqual("(-5, -3) [0, 10] [13, 14] (15, 16)", str(intervals))
class TestIntervalUnion(unittest.TestCase):
    def initialise_intervals(self, low, incl_low, high, incl_high):
        self.intervals = Intervals(low, incl_low, high, incl_high)

    def union(self, low, incl_low, high, incl_high):
        interval = Intervals(low, incl_low, high, incl_high)
        self.intervals.union(interval)

    def assert_interval_matches(self, expected):
        if expected is not None:
            self.assertEqual(expected, str(self.intervals))
        else:
            self.assertIsNone(self.intervals)

    def test_union_no_overlap(self):
        self.initialise_intervals(-100, True, 100, False)
        self.assert_interval_matches("[-100, 100)")

        self.union(200, True, 300, True)
        self.assert_interval_matches("[-100, 100) [200, 300]")

    def test_union_equivalent(self):
        self.initialise_intervals(-100, True, 100, True)
        self.assert_interval_matches("[-100, 100]")

        self.union(-100, True, 100, True)
        self.assert_interval_matches("[-100, 100]")

    def test_union_overlap_lower(self):
        self.initialise_intervals(100, False, 200, True)
        self.assert_interval_matches("(100, 200]")

        self.union(150, True, 300, False)
        self.assert_interval_matches("(100, 300)")

    def test_union_overlap_upper(self):
        self.initialise_intervals(-20, False, -10, True)
        self.assert_interval_matches("(-20, -10]")

        self.union(-10, True, 300, False)
        self.assert_interval_matches("(-20, 300)")

    def test_union_overlap_on_bound(self):
        self.initialise_intervals(20, True, 50, True)
        self.assert_interval_matches("[20, 50]")

        self.union(50, False, 100, False)
        self.assert_interval_matches("[20, 100)")

        self.union(100, True, 150, False)
        self.assert_interval_matches("[20, 150)")

    def test_union_with_both_intervals_extensions(self):
        self.initialise_intervals(-1127, False, 5, False)
        self.assert_interval_matches("(-1127, 5)")

        self.union(5, False, 614, False)
        self.assert_interval_matches("(-1127, 5) (5, 614)")

        self.union(5, False, 613, False)
        self.assert_interval_matches("(-1127, 5) (5, 614)")