示例#1
0
 def __init__(self, mode="directed"):
     '''
     Selects (un)directed graph mode.
     '''
     self._mode = mode if mode == "directed" else "undirected"
     self._adjlist = AdjacencyList()
     log.info("running in mode: {}".format(self._mode))
示例#2
0
 def test_adjacency_matrix(self):
     for table in [
         # no edges
         ([],[],[[]]),
         (["a"], [], [ [inf] ]),
         (["a","b"], [], [ [inf]*2, [inf]*2 ]),
         (["a","b","c"], [], [ [inf]*3, [inf]*3, [inf]*3 ]),
         # with edges (1 node)
         (["a"], [("a","a",1)], [ [1] ]),
         (["a"], [("a","a",2)], [ [2] ]),
         # with edges (2 nodes)
         (["a","b"], [("a","a",1)], [ [1,inf], [inf,inf] ]),
         (["a","b"], [("a","a",1),("a","b",2)], [ [1,2], [inf,inf] ]),
         (["a","b"], [("a","a",1),("a","b",2),("b","a",3)], [ [1,2], [3,inf] ]),
         (["a","b"], [("a","a",1),("a","b",2),("b","a",3),("b","b",4)], [ [1,2], [3,4] ]),
         (["a","b"], [("a","b",2),("b","a",3),("b","b",4)], [ [inf,2], [3,4] ]),
         (["a","b"], [("b","a",3),("b","b",4)], [ [inf,inf], [3,4] ]),
         (["a","b"], [("b","b",4)], [ [inf,inf], [inf,4] ]),
         # with edges (3 nodes)
         (["a","b","c"], [("a","a",9),("b","b",2),("c","c",13)], [ [9,inf,inf], [inf,2,inf], [inf,inf,13] ]),
         (["a","b","c"], [("a","a",9),("a","c",1),("b","b",2),("c","a",7),("c","c",13)], [ [9,inf,1], [inf,2,inf], [7,inf,13] ]),
         # with edges (4 nodes)
         (["a","b","c","d"], [("a","b",1),("b","a",4),("b","d",2),("c","c",11),("d","a",3),("d","b",1)], [ [inf,1,inf,inf], [4,inf,inf,2], [inf,inf,11,inf], [3,1,inf,inf] ]),
     ]:
         in_nodes, in_edges, want = table
         l = AdjacencyList()
         for name in in_nodes:
             l = l.add_node(name)
         for (src, dst, weight) in in_edges:
             l = l.add_edge(src, dst, weight)
         self.assertEqual(l.adjacency_matrix(), want, "Added nodes {}, added edges {}".format(in_nodes, in_edges))
示例#3
0
 def test_edge_cardinality(self):
     for table in [
         ([], [], 0),
         (["a"], [], 0),
         (["a","b"], [], 0),
         (["a"], [("a","a")], 1),
         (["a","b"], [("a","a")], 1),
         (["a","b"], [("a","a"),("a","b")], 2),
         (["a","b"], [("a","a"),("b","a")], 2),
         (["a","b"], [("a","a"),("b","b")], 2),
         (["a","b"], [("a","a"),("a","b"),("b","a")], 3),
         (["a","b"], [("a","a"),("a","b"),("b","b")], 3),
         (["a","b"], [("a","a"),("b","a"),("b","b")], 3),
         (["a","b"], [("a","a"),("a","b"),("b","a"),("b","b")], 4),
         (["a","b","c"], [("a","b"),("b","b"),("b","c"),("c","a"),("c","c")], 5),
         (["a","b","c"], [("a","a"),("a","b"),("b","b"),("b","c"),("c","a"),("c","c")], 6),
         (["a","b","c"], [("a","a"),("a","b"),("a","c"),("b","b"),("b","c"),("c","a"),("c","c")], 7),
         (["a","b","c"], [("a","a"),("a","b"),("a","c"),("b","a"),("b","b"),("b","c"),("c","a"),("c","c")], 8),
         (["a","b","c"], [("a","a"),("a","b"),("a","c"),("b","a"),("b","b"),("b","c"),("c","a"),("c","b"),("c","c")], 9),
     ]:
         in_nodes, in_edges, want = table
         l = AdjacencyList()
         for name in in_nodes:
             l = l.add_node(name)
         for (src, dst) in in_edges:
             l = l.add_edge(src, dst)
         self.assertEqual(l.edge_cardinality(), want, "Added nodes {}, added edges {}".format(in_nodes, in_edges))
示例#4
0
 def test_find_edge(self):
     for table in [
         ([], [], ("a","a"), False),
         ([], [], ("a","b"), False),
         ([], [], ("b","a"), False),
         (["a"], [], ("a","a"), False),
         (["a"], [], ("a","b"), False),
         (["a"], [], ("b","a"), False),
         (["a"], [("a","a")], ("a","a"), True),
         (["a"], [("a","a")], ("a","b"), False),
         (["a"], [("a","a")], ("b","a"), False),
         (["a","b"], [], ("a","a"), False),
         (["a","b"], [], ("a","b"), False),
         (["a","b"], [], ("b","a"), False),
         (["a","b"], [("a","b"),("b","b")], ("a","a"), False),
         (["a","b"], [("a","b"),("b","b")], ("a","b"), True),
         (["a","b"], [("a","b"),("b","b")], ("b","b"), True),
         (["a","b"], [("a","b"),("b","b")], ("b","a"), False),
         (["a","b","c"], [("a","a"),("a","b"),("b","b"),("b","c"),("c","a")], ("a","a"), True),
         (["a","b","c"], [("a","a"),("a","b"),("b","b"),("b","c"),("c","a")], ("a","b"), True),
         (["a","b","c"], [("a","a"),("a","b"),("b","b"),("b","c"),("c","a")], ("a","c"), False),
         (["a","b","c"], [("a","a"),("a","b"),("b","b"),("b","c"),("c","a")], ("b","a"), False),
         (["a","b","c"], [("a","a"),("a","b"),("b","b"),("b","c"),("c","a")], ("b","b"), True),
         (["a","b","c"], [("a","a"),("a","b"),("b","b"),("b","c"),("c","a")], ("b","c"), True),
         (["a","b","c"], [("a","a"),("a","b"),("b","b"),("b","c"),("c","a")], ("c","a"), True),
         (["a","b","c"], [("a","a"),("a","b"),("b","b"),("b","c"),("c","a")], ("c","b"), False),
     ]:
         in_nodes, in_edges, target, want = table
         l = AdjacencyList()
         for name in in_nodes:
             l = l.add_node(name)
         for (src, dst) in in_edges:
             l = l.add_edge(src, dst)
         self.assertEqual(l.find_edge(target[0], target[1]), want, "Added nodes {}, added edges {}, find edge {}".format(in_nodes, in_edges, target))
示例#5
0
 def test_is_empty(self):
     for table in [
         ([], True),
         (["a"], False),
         (["a","b"], False),
         (["b","a"], False),
     ]:
         in_sequence, want = table
         l = AdjacencyList()
         for name in in_sequence:
             l = l.add_node(name)
         self.assertEqual(l.is_empty(), want, "Added nodes {}".format(in_sequence))
示例#6
0
 def test_node_cardinality(self):
     for table in [
         ([], 0),
         (["a"], 1),
         (["a","b"], 2),
         (["a","b","c"], 3),
         (["a","b","c","d"], 4),
     ]:
         in_sequence, want = table
         l = AdjacencyList()
         for name in in_sequence:
             l = l.add_node(name)
         self.assertEqual(l.node_cardinality(), want, "Added nodes {}".format(in_sequence))
示例#7
0
 def test_add_node(self):
     for in_sequence in [
         ([]),
         (["a"]),
         (["a","b"]),
         (["b","a"]),
         (["a","b","c"]),
         (["a","c","b"]),
         (["b","a","c"]),
         (["b","c","a"]),
         (["c","a","b"]),
         (["c","b","a"]),
     ]:
         want = sorted(in_sequence)
         l = AdjacencyList()
         for name in in_sequence:
             l = l.add_node(name)
         self.assertEqual(l.list_nodes(), want, "Added nodes {}".format(in_sequence))
示例#8
0
 def test_find_node(self):
     for table in [
         ([], "a", False),
         (["a"], "a", True),
         (["a"], "b", False),
         (["a"], "B", False),
         (["a","b"], "a", True),
         (["a","b"], "b", True),
         (["a","b"], "c", False),
         (["a","b"], "C", False),
         (["a","b","c"], "a", True),
         (["a","b","c"], "b", True),
         (["a","b","c"], "c", True),
         (["a","b","c"], "d", False),
         (["a","b","c"], "D", False),
     ]:
         in_sequence, target, want = table
         l = AdjacencyList()
         for name in in_sequence:
             l = l.add_node(name)
         self.assertEqual(l.find_node(target), want, "Added nodes {}, find node '{}'".format(in_sequence, target))
示例#9
0
 def test_delete_edges(self):
     for table in [
         # empty
         ([], [], "a", []),
         # 1 node
         (["a"], [], "a", []),
         (["a"], [("a","a",1)], "a", []),
         #(["a"], [("a","a",1)], "b", [("a","a",1)]),
         # 2 nodes
         (["a","b"], [("a","a",1),("a","b",1),("b","a",1),("b","b",1)], "a", [("a","b",1),("b","b",1)]),
         (["a","b"], [("a","a",1),("a","b",1),("b","a",1),("b","b",1)], "b", [("a","a",1),("b","a",1)]),
         (["a","b"], [("a","a",1),("a","b",1),("b","a",1),("b","b",1)], "c", [("a","a",1),("a","b",1),("b","a",1),("b","b",1)]),
         # 3 nodes
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1),("c","a",1),("c","b",1),("c","c",1)], "a", [("a","b",1),("a","c",1),("b","b",1),("b","c",1),("c","b",1),("c","c",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1),("c","a",1),("c","b",1),("c","c",1)], "b", [("a","a",1),("a","c",1),("b","a",1),("b","c",1),("c","a",1),("c","c",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1),("c","a",1),("c","b",1),("c","c",1)], "c", [("a","a",1),("a","b",1),("b","a",1),("b","b",1),("c","a",1),("c","b",1)]),
     ]:
         in_nodes, in_edges, target, want = table
         l = AdjacencyList()
         for name in in_nodes:
             l = l.add_node(name)
         for (src, dst, weight) in in_edges:
             l = l.add_edge(src, dst, weight)
         l = l.delete_edges(target)
         self.assertEqual(l.list_edges(), want, "Added nodes {}, added edges {}, deleted edges towards node {}".format(in_nodes, in_edges, target))
示例#10
0
 def test_delete_node(self):
     for table in [
         ([], "a", []),
         (["a"], "a", []),
         (["a"], "b", ["a"]),
         (["a"], "B", ["a"]),
         (["a","b"], "a", ["b"]),
         (["a","b"], "b", ["a"]),
         (["a","b"], "c", ["a","b"]),
         (["a","b"], "C", ["a","b"]),
         (["a","b","c"], "a", ["b","c"]),
         (["a","b","c"], "b", ["a","c"]),
         (["a","b","c"], "c", ["a","b"]),
         (["a","b","c"], "d", ["a","b","c"]),
         (["a","b","c"], "D", ["a","b","c"]),
     ]:
         in_sequence, target, want = table
         l = AdjacencyList()
         for name in in_sequence:
             l = l.add_node(name)
         l = l.delete_node(target)
         self.assertEqual(l.list_nodes(), want, "Added nodes {}, deleted '{}'".format(in_sequence, target))
示例#11
0
 def test_add_edge_update(self):
     for table in [
         # 1 node
         (["a"], [], []),
         (["a"], [("a","a",1),("a","a",2)], [("a","a",2)]),
         # 2 nodes
         (["a","b"], [("a","a",1), ("a","a",2)], [("a","a",2)]),
         (["a","b"], [("a","a",1), ("a","b",1),("b","a",1), ("a","a",2)], [("a","a",2),("a","b",1),("b","a",1)]),
         (["a","b"], [("a","a",1), ("a","b",1),("b","a",1), ("a","b",2)], [("a","a",1),("a","b",2),("b","a",1)]),
         (["a","b"], [("a","a",1), ("a","b",1),("b","a",1), ("b","a",2)], [("a","a",1),("a","b",1),("b","a",2)]),
         # 3 nodes
         (["a","b","c"], [("a","b",1),("b","b",1),("b","c",1),("a","b",2)], [("a","b",2),("b","b",1),("b","c",1)]),
         (["a","b","c"], [("a","b",1),("b","b",1),("b","c",1),("b","b",2)], [("a","b",1),("b","b",2),("b","c",1)]),
         (["a","b","c"], [("a","b",1),("b","b",1),("b","c",1),("b","c",2)], [("a","b",1),("b","b",1),("b","c",2)]),
     ]:
         in_nodes, in_edges, want = table
         l = AdjacencyList()
         for name in in_nodes:
             l = l.add_node(name)
         for (src, dst, weight) in in_edges:
             l = l.add_edge(src, dst, weight)
         self.assertEqual(l.list_edges(), want, "Added nodes {}, added edges {}".format(in_nodes, in_edges))
示例#12
0
 def test_add_edge(self):
     for table in [
         # no nodes
         ([], [("a","a",1)], []),
         ([], [("a","b",1)], []),
         ([], [("b","a",1)], []),
         # 1 node
         (["a"], [], []),
         (["a"], [("a","a",1)], [("a","a",1)]),
         (["a"], [("a","b",1)], []),
         (["a"], [("b","a",1)], []),
         # 2 nodes
         (["a","b"], [], []),
         (["a","b"], [("a","a",1)], [("a","a",1)]),
         (["a","b"], [("a","a",1), ("a","a",1)], [("a","a",1)]),
         (["a","b"], [("a","a",1), ("a","b",1)], [("a","a",1),("a","b",1)]),
         (["a","b"], [("a","a",1), ("a","b",1),("b","a",1)], [("a","a",1),("a","b",1),("b","a",1)]),
         # 3 nodes
         (["a","b","c"], [], []),
         (["a","b","c"], [("a","a",1)], [("a","a",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1)], [("a","a",1),("a","b",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1)], [("a","a",1),("a","b",1),("a","c",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1),("b","a",1)], [("a","a",1),("a","b",1),("a","c",1),("b","a",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1)], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1)], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1),("c","a",1)], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1),("c","a",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1),("c","a",1),("c","b",1)], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1),("c","a",1),("c","b",1)]),
         (["a","b","c"], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1),("c","a",1),("c","b",1),("c","c",1)], [("a","a",1),("a","b",1),("a","c",1),("b","a",1),("b","b",1),("b","c",1),("c","a",1),("c","b",1),("c","c",1)]),
     ]:
         in_nodes, in_edges, want = table
         for _ in range(10):
             random.Random(1337).shuffle(in_edges)
             l = AdjacencyList()
             for name in in_nodes:
                 l = l.add_node(name)
             for (src, dst, weight) in in_edges:
                 l = l.add_edge(src, dst, weight)
             self.assertEqual(l.list_edges(), want, "Added nodes {}, added edges {}".format(in_nodes, in_edges))
示例#13
0
class TerminalUI:
    def __init__(self, mode="directed"):
        '''
        Selects (un)directed graph mode.
        '''
        self._mode = mode if mode == "directed" else "undirected"
        self._adjlist = AdjacencyList()
        log.info("running in mode: {}".format(self._mode))

    def run(self):
        '''
        Provides a terminal-based UI to perform graph operations.
        '''
        self.display_menu()
        while True:
            opt, err = self.get_choice()
            if err is not None:
                self.display_error(err)
                continue

            if opt == "m":
                self.display_menu()
            elif opt == "v":
                self.display_graph()
            elif opt == "a":
                self.add_node()
            elif opt == "b":
                self.add_edge()
            elif opt == "d":
                self.delete_node()
            elif opt == "r":
                self.delete_edge()
            elif opt == "f":
                self.find_node()
            elif opt == "g":
                self.find_edge()
            elif opt == "D":
                self.dijkstra()
            elif opt == "F":
                self.floyd()
            elif opt == "W":
                self.warshall()
            elif opt == "P":
                self.prim()
            elif opt == "q":
                break
            else:
                log.error("menu case '{}' is missing, aborting".format(opt))
                return 1

    def menu_options(self):
        '''
        Returns a list of printable menu options.  Blank entries are interpreted
        as new lines, and single characters before the colon as hotkeys.
        '''
        return [
            "m: menu",
            "v: view",
            "q: quit",
            "",
            "a: add node",
            "b: add edge",
            "",
            "d: delete node",
            "r: delete edge",
            "",
            "f: find node",
            "g: find edge",
            "",
            "W: Warshall",
            "F: Floyd",
            "D: Dijkstra",
            "P: Prim",
        ]

    def display_menu(self):
        '''
        Shows a menu which is encapsulated between a top rule and a bottom rule.
        '''
        print(self.menu_rule("top", self.menu_width()))
        for opt in self.menu_options():
            print("\t{}".format(opt))
        print(self.menu_rule("bot", self.menu_width()))

    def menu_rule(self, pos, width):
        '''
        Returns a horizontal line using stars or tildes.
        '''
        return ("*" if pos == "top" else "~") * width

    def menu_width(self):
        '''
        Returns the menu width.
        '''
        return 32

    def menu_hotkeys(self):
        '''
        Returns a list of symbols that the menu defined as valid hotkeys.
        '''
        opts = self.menu_options()
        return [o.split(":")[0] for o in opts if len(o.split(":")[0]) == 1]

    def get_choice(self):
        '''
        Attempts to read a valid menu option from the user.
        '''
        c, err = self.get_char("menu")
        if err is not None:
            return None, err
        if c not in self.menu_hotkeys():
            return None, "invalid choice"
        return c, None

    def get_node(self, msg, want):
        '''
        Attempts to read a valid node name from the user.  If `want` is False
        (True), an error is returned if the entered node is a (non-)member.
        '''
        name, err = self.get_char(msg)
        if err is not None:
            return None, err
        if want != self._adjlist.find_node(name):
            return None, "node '{}' is a {}member".format(
                name, "non-" if want else "")
        return name, None

    def get_char(self, message):
        '''
        Writes a message to stdout and waits for one-character from stdin.
        '''
        buf = input("{}> ".format(message))
        if len(buf) != 1:
            return None, "invalid input (not a single character)"
        return buf, None

    def get_int(self, message):
        '''
        Writes a message to stdout and waits for an integer from stdin.
        '''
        buf = input("{}> ".format(message))
        try:
            return int(buf), None
        except ValueError:
            return None, "invalid input (not an integer)"

    def get_weight(self, message, low, high):
        '''
        Writes a message to stdout and waits for an integer in [low,high].
        '''
        weight, err = self.get_int(message)
        if err is not None:
            return None, err
        if weight < low or weight > high:
            return None, "invalid input (weight must be in [{},{}])".format(
                low, high)
        return weight, None

    def display_graph(self):
        '''
        Displays graph info.
        '''
        if self._adjlist.is_empty():
            self.display_empty()
            return

        nodes = self._adjlist.list_nodes()
        log.debug("all nodes: {}".format(nodes))
        log.debug("all edges: {}".format(self._adjlist.list_edges()))
        self.display_matrix_head(nodes)
        self.display_matrix_data(nodes, self._adjlist.adjacency_matrix())
        self.display_cardinality()

    def add_node(self):
        '''
        Let the user add a node to the graph.
        '''
        name, err = self.get_node("Enter node name", False)
        if err is not None:
            self.display_error(err)
            return

        self._adjlist = self._adjlist.add_node(name)

    def delete_node(self):
        '''
        Let the user delete add a node from the graph.
        '''
        name, err = self.get_node("Enter node name", True)
        if err is not None:
            self.display_error(err)
            return

        self._adjlist = self._adjlist.delete_edges(name)
        self._adjlist = self._adjlist.delete_node(name)

    def add_edge(self):
        '''
        Let the user add an edge to the graph.
        '''
        from_node, err = self.get_node("Enter from node", True)
        if err is not None:
            self.display_error(err)
            return

        to_node, err = self.get_node("Enter to node", True)
        if err is not None:
            self.display_error(err)
            return

        weight, err = self.get_weight("Enter weight", 1, 99)
        if err is not None:
            self.display_error(err)
            return

        self.adj_list = self._adjlist.add_edge(from_node, to_node, weight)
        if self._mode == "undirected":
            self._adjlist = self._adjlist.add_edge(to_node, from_node, weight)

    def delete_edge(self):
        '''
        Let the user delete an edge from the graph.
        '''
        from_node, err = self.get_node("Enter from node", True)
        if err is not None:
            self.display_error(err)
            return

        to_node, err = self.get_node("Enter to node", True)
        if err is not None:
            self.display_error(err)
            return

        if not self._adjlist.find_edge(from_node, to_node):
            self.display_error("edge ({},{}) is non-member".format(
                from_node, to_node))
            return

        self._adjlist = self._adjlist.delete_edge(from_node, to_node)
        if self._mode == "undirected":
            self._adjlist = self._adjlist.delete_edge(to_node, from_node)

    def find_node(self):
        '''
        Let the user search for a node in the graph.
        '''
        name, err = self.get_char("Enter node name")
        if err is not None:
            self.display_error(err)
            return

        if self._adjlist.find_node(name):
            self.display_member_node(name)
        else:
            self.display_nonmember_node(name)

    def find_edge(self):
        '''
        Let the user search for an edge in the graph.
        '''
        from_node, err = self.get_char("Enter from node")
        if err is not None:
            self.display_error(err)
            return

        to_node, err = self.get_char("Enter to node")
        if err is not None:
            self.display_error(err)
            return

        if self._adjlist.find_edge(from_node, to_node):
            self.display_member_edge(from_node, to_node)
        else:
            self.display_nonmember_edge(from_node, to_node)

    def warshall(self):
        '''
        Run Warshall's algorithm.
        '''
        if self._adjlist.is_empty():
            self.display_error("graph is empty")
            return

        nodes = self._adjlist.list_nodes()
        self.display_matrix_head(nodes)
        self.display_matrix_data(nodes, warshall(self._adjlist))

    def floyd(self):
        '''
        Run Floyds's algorithm.
        '''
        if self._adjlist.is_empty():
            self.display_error("graph is empty")
            return

        nodes = self._adjlist.list_nodes()
        self.display_matrix_head(nodes)
        self.display_matrix_data(nodes, floyd(self._adjlist))

    def dijkstra(self):
        '''
        Run Dijkstra's algorithm.
        '''
        if self._adjlist.is_empty():
            self.display_error("graph is empty")
            return
        start_node, err = self.get_node("Enter start node", True)
        if err is not None:
            self.display_error(err)
            return

        dist, prev = dijkstra(self._adjlist, start_node)
        self.display_sequence_head(self._adjlist.list_nodes())
        self.display_sequence_data([
            ("distance", dist, None),
            ("previous", prev, None),
        ])

    def prim(self):
        '''
        Run Prim's algorithm.
        '''
        if self._mode == "directed":
            self.error("invalid graph mode")
            return
        if self._adjlist.is_empty():
            self.display_error("graph is empty")
            return
        start_node, err = self.get_node("Enter start node", True)
        if err is not None:
            self.display_error(err)
            return

        lowcost, closest = prim(self._adjlist, start_node)
        self.display_sequence_head(self._adjlist.list_nodes())
        self.display_sequence_data([
            ("lowcost", lowcost, None),
            ("closest", closest, None),
        ])
        self.display_mst_sum(lowcost)

    def display_mst_sum(self, lowcost):
        mst_sum = sum([v for v in lowcost if v is not None and v != inf])
        print("\tMST sum: {}\n".format(mst_sum))

    def display_empty(self):
        print("\n\tGraph is empty\n")

    def display_member_node(self, name):
        print("\tNode {} is a member".format(name))

    def display_nonmember_node(self, name):
        print("\tNode {} is a non-member".format(name))

    def display_member_edge(self, from_node, to_node):
        print("\tEdge ({},{}) is a member".format(from_node, to_node))

    def display_nonmember_edge(self, from_node, to_node):
        print("\tEdge ({},{}) is a non-member".format(from_node, to_node))

    def display_sequence_head(self, nodes):
        print("\n {: ^8}#".format(""), end="")
        for node in nodes:
            print(" {: ^3} ".format(node), end="")
        print("\n ========#" + "=" * 5 * len(nodes))

    def display_sequence_data(self, data):
        for (name, sequence, star_val) in data:
            print(" {: >8}#".format(name), end="")
            for v in sequence:
                print(" {: ^3} ".format("*" if v == star_val else v), end="")
            print("")
        print("")

    def display_matrix_head(self, nodes):
        print("\n {: ^3}|".format(""), end="")
        for node in nodes:
            print(" {: ^3} ".format(node), end="")
        print("\n----+" + "-" * 5 * len(nodes))

    def display_matrix_data(self, nodes, matrix):
        for name, row in zip(nodes, matrix):
            print(" {: >3}|".format(name), end="")
            for col in row:
                print(" {: ^3} ".format("*" if col == inf else col), end="")
            print("")
        print("")

    def display_cardinality(self):
        node_cardinality = self._adjlist.node_cardinality()
        edge_cardinality = self._adjlist.edge_cardinality()
        if self._mode == "undirected":
            self_loops = self._adjlist.self_loops()
            edge_cardinality = int((edge_cardinality - self_loops) / 2 +
                                   self_loops)
        print("node cardinality: {}".format(node_cardinality))
        print("edge cardinality: {}".format(edge_cardinality))
        print("")

    def display_error(self, err):
        print("error> {}".format(err))
def prim(adjlist, start_node):
    '''
    Returns the result of running Prim's algorithm as two N-length lists:
    1) lowcost l: here, l[i] contains the weight of the cheapest edge to connect
    the i:th node to the minimal spanning tree that started at `start_node`.
    2) closest c: here, c[i] contains the node name that the i:th node's
    cheapest edge orignated from. 

    If the index i refers to the start node, set the associated values to None.

    Pre: adjlist is setup as an undirected graph and start_node is a member.

    === Example ===
    Suppose that we have the following adjacency matrix:

      a b c
    -+-----
    a|* 1 3
    b|1 * 1
    c|3 1 *

    For start node "a", the expected output would then be:

    l: [ None, 1, 1]
    c: [ None, 'a', 'b' ]
    '''
    #log.info("TODO: prim()")

    print("nEW")
    connected = [start_node]
    tree = AdjacencyList()  # keeps track of added nodes
    tree.add_node(start_node)

    # create initial edge and node
    while are_unconnected(adjlist, connected):
        curr_edge = Edge(
            weight=inf)  # will be overridden by first legible node
        nod = adjlist
        while not nod.is_empty():  # find currently node with shortest edge

            edg = nod.edges()
            while not edg.is_empty():  # find best edge in node
                if not edg.dst() in connected and edg.weight(
                ) < curr_edge.weight() and edg.dst() != nod.name():
                    # better edge found, saving
                    curr_node = nod
                    curr_edge = edg
                edg = edg.tail()

            nod = nod.tail()

        print("edge to {} with weight {} from {}".format(
            curr_edge.dst(), curr_edge.weight(), curr_node.name()))

        # shortest edge found, find the node in the parallell tre

        nud = tree
        while not nud.is_empty():
            if nud.name() == curr_node.name():
                nud.edges().add(curr_edge.dst(), curr_edge.weight())
            nud = nud.tail()

        tree.add_node(curr_edge.dst())
        # find corresponding node for undirectional edge
        """
        nad = tree
        while not nad.is_empty():
            if nad.name() == curr_edge.dst():
                nad.edges().add(curr_node.name(), curr_edge.weight())
            nad = nad.tail()
        """
        connected.append(curr_edge.dst())
        input()

        print(connected)
    print(connected)
    n = adjlist.node_cardinality()
    l = [inf] * n
    c = [None] * n

    nodelist = adjlist.list_nodes()
    for index, item in enumerate(nodelist):
        if not item in connected:
            l[index] = inf
            c[index] = None
            continue
        elif item == start_node:
            l[index] = None
            c[index] = None
            continue
    nad = tree
    while not nad.is_empty():
        edg = nad.edges()
        while not edg.is_empty():
            l[nodelist.index(edg.dst())] = edg.weight()
            c[nodelist.index(edg.dst())] = nad.name()
            edg = edg.tail()
        nad = nad.tail()

    print("prim prom")
    print(l)
    print("--")
    print(c)
    return l, c