def test_BoundaryTracker_get_spans_from_node(self):
        # Setup boundaries for 1st three chords in diagram
        diag = Diagram([(1, 4), (2, 5), (3, 8), (6, 7)])

        bounds = BoundaryTracker()
        region_factory = RegionFactory()

        outside = region_factory.get_next_region_id()
        bounds.add(Span(1, 8), outside, outside)  # Initially all nodes outside

        # Add chord (1,4)
        regionA = region_factory.get_next_region_id()
        bounds.add(Span(1, 4), outside, regionA)

        # Add chord (2,5) - segment (4,5)
        regionB = region_factory.get_next_region_id()
        bounds.add(Span(4, 5), outside, regionB)
        # Test available spans to close (4,5) regionB
        spans = bounds.get_spans_from_node(4, diag, 5)
        self.assertEqual(spans, [[1, 2]])
        bounds.add(Span(1, 2), regionA, regionB)

        # Add chord (6, 7) (next in numeric order) span(6,7)
        # It's a 1-move loop, region is already closed
        regionC = region_factory.get_next_region_id()
        bounds.add(Span(6, 7), regionA, regionC)
        bounds.add(Span(5, 6), regionA, regionA)

        # Add chord(3, 8), span(7, 8)
        regionD = region_factory.get_next_region_id()
        bounds.add(Span(7, 8), regionA, regionD)
        # Test available spans to close (7,8) regionD
        spans = bounds.get_spans_from_node(7, diag, 8)
        self.assertEqual(spans, [[5, 6], [6, 7]])
def diagram_filter(orig_sparse_diag, extension_chord, extended_sparse_diag):
    diag = Diagram(extended_sparse_diag)
    diag.compress()
    if (contains_one_move(diag) or
        contains_three_move(diag)):
        return False # Skip this diagram
    return True # Use this diagram
 def test_get_span_complement(self):
     diag = Diagram([(1, 4), (2, 5), (3, 8), (6, 7)])
     span = Span(4, 5)
     cspan = diag.get_span_complement(span)
     self.assertEqual(cspan, Span(1, 2))
     ccspan = diag.get_span_complement(cspan)
     self.assertEqual(span, ccspan)  # Double complement returns original
    def test_BoundaryTracker_get_regions_for_node(self):

        diag = Diagram([(1, 4), (2, 5), (3, 8), (6, 7)])

        bounds = BoundaryTracker()
        region_factory = RegionFactory()

        outside = region_factory.get_next_region_id()
        bounds.add(Span(1, 8), outside, outside)  # Initially all nodes outside

        # Add chord (1,4)
        regionA = region_factory.get_next_region_id()
        bounds.add(Span(1, 4), outside, regionA)

        # Add chord (2,5) - segment (4,5)
        regionB = region_factory.get_next_region_id()
        bounds.add(Span(4, 5), outside, regionB)
        bounds.add(Span(1, 2), regionA, regionB)

        self.assertEqual(([['A', 'B'], ['@', 'A']]),
                         bounds.get_regions_for_node(2))
        with self.assertRaisesRegexp(
                Exception,
                "get_regions_for_node: No boundary found containing node: 17"):
            bounds.get_regions_for_node(17)
def check_diagram(sparse_diagram, ext_possible_chords, ext_min, ext_max):

    # Skip diagrams with 1, 2 or 3 moves
    compressed_diag = get_compressed_diagram(sparse_diagram)
    if (contains_one_move(compressed_diag) or
        contains_two_move(compressed_diag) or
        contains_three_move(compressed_diag)):
        return (sparse_diagram, [], [])

    # Find the permitted single chord extensions
    invariant_chords = get_invariant_chords_for_diagram(
            sparse_diagram, ext_possible_chords, diagram_filter=diagram_filter,
            doScale=False)
    if len(invariant_chords) == 0:
        # Skip diagrams with no invariant chords
        return (sparse_diagram, invariant_chords, [])

    # Test planarability of sparse_diag plus combinations of min..max chords
    planarable_extension_sets = []
    for n_extensions in range(ext_min, ext_max+1):
        for extensions in combinations(invariant_chords, n_extensions):
            # Skip extension chord pair if they share any nodes
            uniq_nodes = {node for ext in extensions for node in ext}
            if len(uniq_nodes) != n_extensions * 2:
                continue
            diag = Diagram(sparse_diagram + list(extensions))
            if diag.is_planarable():
                planarable_extension_sets.append((timestamp(), extensions))

    return (sparse_diagram, invariant_chords, planarable_extension_sets)
def check_planarable_2move(diagram):
    # Scale up by 3 to leave 2 open nodes between each existing node
    diagx3 = Diagram(diagram)
    diagx3.scale(3)

    # Find parallel chord to each existing chord
    n_chords = diagx3.size()
    chords = [ get_parallel_extension(chord, n_chords*2*3) for chord in diagx3 ]

    n_extensions = n_chords # Only check with 1 extension per chord
    chord_combos = combinations(chords, n_extensions)
    for chord_combo in chord_combos:
        # Append this chord_combo to new copy of diagx3
        test_diag = Diagram(diagx3)
        for chord in chord_combo:
            test_diag.append(chord)
        if test_diag.is_planarable():
            return (diagram, test_diag, chord_combo)

    return (diagram, None, None)
def check_123_planarable(diagram):

    # Skip diagrams with 1, 2 or 3 moves
    if (contains_one_move(diagram) or
        contains_two_move(diagram) or
        contains_three_move(diagram)):
        return (diagram, False)

    # Test planarability of sparse_diag
    diag = Diagram(diagram)
    rv = diag.is_planarable()

    return (diagram, rv)
    def test_BoundaryTracker_validate_all_regions_closed_3_chord(self):
        "Verify regression on diagram: [(1, 2), (3, 6), (4, 5)]"
        diag = Diagram([(1, 2), (3, 6), (4, 5)])
        bounds = BoundaryTracker()
        region_factory = RegionFactory()

        outside = region_factory.get_next_region_id()
        regionA = region_factory.get_next_region_id()
        regionB = region_factory.get_next_region_id()
        regionC = region_factory.get_next_region_id()

        bounds.add(Span(1, 6), outside, outside)

        bounds.add(Span(1, 2), outside, regionA)
        bounds.add(Span(4, 5), outside, regionB)
        bounds.add(Span(3, 4), outside, regionC)
        bounds.add(Span(5, 6), outside, regionC)
        self.assertTrue(bounds.validate_all_regions_closed(diag))
    def test_BoundaryTracker_order_spans_in_closed_loop(self):
        diag = Diagram([(1, 4), (2, 5), (3, 8), (6, 7)])
        spans = (Span(1, 2), Span(2, 3), Span(3, 4))
        self.assertEqual(
            BoundaryTracker.order_spans_in_closed_loop(spans, diag),
            [[3, 4], [1, 2], [2, 3]])

        spans = (Span(3, 4), Span(1, 2), Span(7, 8), Span(5, 6))
        self.assertEqual(
            BoundaryTracker.order_spans_in_closed_loop(spans, diag),
            [[5, 6], [7, 8], [3, 4], [1, 2]])

        spans = [Span(6, 7)]
        self.assertEqual(
            BoundaryTracker.order_spans_in_closed_loop(spans, diag), [[6, 7]])

        spans = [Span(1, 2), Span(5, 6)]  # Incomplete loop
        self.assertEqual(
            BoundaryTracker.order_spans_in_closed_loop(spans, diag), None)

        spans = [Span(1, 2), Span(4, 5), Span(2, 3)]  # Loop plus extra span
        self.assertEqual(
            BoundaryTracker.order_spans_in_closed_loop(spans, diag), None)
    def test_BoundaryTracker_get_spans_along_region(self):
        "Verify get_spans_along_region used as generator"
        diag = Diagram([[1, 8], [2, 9], [3, 12], [4, 7], [5, 10], [6, 11]])
        bounds = BoundaryTracker()

        outside = bounds.region_factory.get_next_region_id()
        regionA = bounds.region_factory.get_next_region_id()
        regionB = bounds.region_factory.get_next_region_id()

        bounds.add(Span(1, 12), outside, outside)
        bounds.add(Span(4, 7), regionA, outside, Chord([4, 7]))
        bounds.add(Span(7, 8), regionB, outside, Chord([1, 8]))

        locator = outside
        new_region = regionB
        generator = bounds.gen_region_bounds(Span(7, 8), 8, locator,
                                             new_region, diag)
        expected = (
            # Result 1: Simple region, closed on same side
            ({
                '@_@': [[8, 12]],
                '@_A': [[4, 7]],
                '@_B': [[1, 4], [7, 8]]
            }, [[7, 8], [3, 4], [2, 3], [1, 2]]),
            # Result 2: Complex region, up and over the top
            ({
                '@_@': [[8, 12]],
                '@_B': [[1, 4], [7, 8]],
                'A_B': [[4, 7]]
            }, [[7, 8], [4, 5], [5, 6], [6, 7], [3, 4], [2, 3], [1, 2]]))
        count = 0
        for (result_pair, expected_pair) in zip(generator, expected):
            count += 1
            self.assertEqual(result_pair[0].boundaries, expected_pair[0])
            self.assertEqual(result_pair[1], expected_pair[1])
        self.assertEqual(count, 2)
    def test_BoundaryTracker_validate_all_regions_closed(self):
        # Validate regions after adding boundaries for each chord
        diag = Diagram([(1, 4), (2, 5), (3, 8), (6, 7)])
        bounds = BoundaryTracker()
        region_factory = RegionFactory()

        outside = region_factory.get_next_region_id()
        bounds.add(Span(1, 8), outside, outside)  # Initially all nodes outside
        self.assertTrue(bounds.validate_all_regions_closed(diag))

        # Add chord (1,4)
        regionA = region_factory.get_next_region_id()
        bounds.add(Span(1, 4), outside, regionA)
        self.assertTrue(bounds.validate_all_regions_closed(diag))

        # Add chord (2,5) - segment (4,5)
        regionB = region_factory.get_next_region_id()
        bounds.add(Span(4, 5), outside, regionB)
        bounds.add(Span(1, 2), regionA, regionB)
        self.assertTrue(bounds.validate_all_regions_closed(diag))

        # Add chord (6, 7) (next in numeric order) span(6,7)
        # It's a 1-move loop, region is already closed
        regionC = region_factory.get_next_region_id()
        bounds.add(Span(6, 7), regionA, regionC)
        bounds.add(Span(5, 6), regionA, regionA)
        self.assertTrue(bounds.validate_all_regions_closed(diag))

        # Add chord(3, 8), span(7, 8)
        regionD = region_factory.get_next_region_id()
        bounds.add(Span(7, 8), regionA, regionD)
        self.assertFalse(bounds.validate_all_regions_closed(diag))
        bounds.add(Span(5, 6), regionA, regionD)
        self.assertFalse(bounds.validate_all_regions_closed(diag))
        bounds.add(Span(2, 3), outside, regionD)
        self.assertTrue(bounds.validate_all_regions_closed(diag))
 def test_Diagram_is_planarable_5_chord_regression4(self):
     # This is True
     diag = Diagram([[1, 3], [2, 10], [4, 7], [5, 8], [6, 9]])
     self.assertFalse(diag.is_planarable())
 def test_Diagram_is_planarable_6_chord_regression1(self):
     # This is True
     diag = Diagram([[1, 8], [2, 9], [3, 12], [4, 7], [5, 10], [6, 11]])
     self.assertTrue(diag.is_planarable())
 def test_Diagram_is_planarable_5_chord_regression2(self):
     "Rotation of test 5_chord_regression1"
     # This is False
     diag = Diagram([[1, 7], [2, 3], [4, 9], [5, 8], [6, 10]])
     self.assertFalse(diag.is_planarable())
 def test_Diagram_is_planarable_5_chord_regression3(self):
     # This is True
     diag = Diagram([[1, 8], [2, 5], [3, 10], [4, 9], [6, 7]])
     self.assertTrue(diag.is_planarable())
 def test_Diagram_is_planarable_4_chord_test1(self):
     diag = Diagram([(1, 4), (2, 3), (5, 8), (6, 7)])
     self.assertTrue(diag.is_planarable())
 def test_Diagram_is_planarable_5_chord_regression1(self):
     diag = Diagram([(1, 2), (3, 8), (4, 7), (5, 9), (6, 10)])
     self.assertFalse(diag.is_planarable())
 def test_Diagram_is_planarable_4_chord_regression2(self):
     diag = Diagram([[1, 6], [2, 3], [4, 7], [5, 8]])
     self.assertTrue(diag.is_planarable())
 def test_Diagram_is_planarable_4_chord_regression3(self):
     diag = Diagram([(1, 8), (2, 6), (3, 5), (4, 7)])
     self.assertFalse(diag.is_planarable())
 def test_Diagram_is_planarable_6_chord_regression3(self):
     diag = Diagram([[1, 4], [2, 5], [3, 12], [6, 9], [7, 11], [8, 10]])
     self.assertFalse(diag.is_planarable())
 def test_remove_one_move(self):
     diag = Diagram([(1, 6), (2, 3), (4, 7), (5, 8)])
     diag.remove_one_moves()
     final_diag = Diagram([(1, 4), (2, 5), (3, 6)])
     self.assertEqual(diag, final_diag)
 def test_Diagram_is_planarable_21_chord_1(self):
     diag = Diagram([(1, 42), (2, 37), (3, 36), (4, 31), (5, 30), (6, 25),
                     (7, 24), (8, 23), (9, 26), (10, 29), (11, 32),
                     (12, 35), (13, 38), (14, 41), (15, 40), (16, 39),
                     (17, 34), (18, 33), (19, 28), (20, 27), (21, 22)])
     self.assertTrue(diag.is_planarable())
 def test_Diagram_is_planarable_3_chord_positive(self):
     diag = Diagram([(1, 2), (3, 6), (4, 5)])
     self.assertTrue(diag.is_planarable())
 def test_Diagram_is_planarable_7_chord(self):
     diag = Diagram([(1, 10), (2, 7), (3, 6), (4, 11), (5, 12), (13, 8),
                     (14, 9)])
     self.assertTrue(diag.is_planarable())
 def test_Diagram_is_planarable_2_move_crossed(self):
     "Verify that the crossed 2-move fails in all rotations"
     for _ in range(0, 4):
         diag = Diagram([(1, 3), (2, 4)])
         self.assertFalse(diag.is_planarable())
 def test_Diagram_is_planarable_maindiag(self):
     diag = Diagram([(1, 4), (2, 5), (3, 8), (6, 7)])
     self.assertTrue(diag.is_planarable())
 def test_Diagram_is_planarable_6_chord_regression4(self):
     diag = Diagram([[1, 10], [2, 7], [3, 6], [4, 13], [5, 12], [8, 11],
                     [9, 14]])
     self.assertTrue(diag.is_planarable())
 def test_Diagram_is_planarable_4_chord_regression1(self):
     diag = Diagram([[1, 6], [2, 5], [3, 7], [4, 8]])
     self.assertFalse(diag.is_planarable())
 def test_Diagram_is_planarable_3_chord_regression1(self):
     diag = Diagram([(1, 4), (2, 6), (3, 5)])
     self.assertFalse(diag.is_planarable())
 def test_Diagram_is_planarable_3_chord_regression4(self):
     'Form region opposite the typical - but works - interesting'
     diag = Diagram([[1, 4], [6, 2], [3, 5]])
     self.assertFalse(diag.is_planarable())