# Copyright 2010-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

from portage.tests import TestCase
from portage.util.digraph import digraph
#~ from portage.util import noiselimit
import portage.util

class DigraphTest(TestCase):

	def _assertBFSEqual(self, result, expected):
		result_stack = list(result)
		result_stack.reverse()
		expected_stack = list(reversed(expected))
		result_compared = []
		expected_compared = []
		while result_stack:
			if not expected_stack:
				result_compared.append(result_stack.pop())
				self.assertEqual(result_compared, expected_compared)
			expected_set = expected_stack.pop()
			if not isinstance(expected_set, list):
				expected_set = [expected_set]
			expected_set = set(expected_set)
			while expected_set:
				if not result_stack:
					expected_compared.extend(expected_set)
					self.assertEqual(result_compared, expected_compared)
				obj = result_stack.pop()
				try:
					expected_set.remove(obj)
				except KeyError:
					expected_compared.extend(expected_set)
					result_compared.append(obj)
					self.assertEqual(result_compared, expected_compared)
				else:
					expected_compared.append(obj)
					result_compared.append(obj)
		if expected_stack:
			expected_set = expected_stack.pop()
			if not isinstance(expected_set, list):
				expected_set = [expected_set]
			expected_compared.extend(expected_set)
			self.assertEqual(result_compared, expected_compared)

	def testBackwardCompatibility(self):
		g = digraph()
		f = g.copy()
		g.addnode("A", None)
		self.assertEqual("A" in g, True)
		self.assertEqual(bool(g), True)
		self.assertEqual(g.allnodes(), ["A"])
		self.assertEqual(g.allzeros(), ["A"])
		self.assertEqual(g.hasnode("A"), True)

	def testDigraphEmptyGraph(self):
		g = digraph()
		f = g.clone()
		for x in g, f:
			self.assertEqual(bool(x), False)
			self.assertEqual(x.contains("A"), False)
			self.assertEqual(x.firstzero(), None)
			self.assertRaises(KeyError, x.remove, "A")
			x.delnode("A")
			self.assertEqual(list(x), [])
			self.assertEqual(x.get("A"), None)
			self.assertEqual(x.get("A", "default"), "default")
			self.assertEqual(x.all_nodes(), [])
			self.assertEqual(x.leaf_nodes(), [])
			self.assertEqual(x.root_nodes(), [])
			self.assertRaises(KeyError, x.child_nodes, "A")
			self.assertRaises(KeyError, x.parent_nodes, "A")
			self.assertEqual(x.hasallzeros(), True)
			self.assertRaises(KeyError, list, x.bfs("A"))
			self.assertRaises(KeyError, x.shortest_path, "A", "B")
			self.assertRaises(KeyError, x.remove_edge, "A", "B")
			self.assertEqual(x.get_cycles(), [])
			x.difference_update("A")
			portage.util.noiselimit = -2
			x.debug_print()
			portage.util.noiselimit = 0

	def testDigraphCircle(self):
		g = digraph()
		g.add("A", "B", -1)
		g.add("B", "C", 0)
		g.add("C", "D", 1)
		g.add("D", "A", 2)

		f = g.clone()
		for x in g, f:
			self.assertEqual(bool(x), True)
			self.assertEqual(x.contains("A"), True)
			self.assertEqual(x.firstzero(), None)
			self.assertRaises(KeyError, x.remove, "Z")
			x.delnode("Z")
			self.assertEqual(list(x), ["A", "B", "C", "D"])
			self.assertEqual(x.get("A"), "A")
			self.assertEqual(x.get("A", "default"), "A")
			self.assertEqual(x.all_nodes(), ["A", "B", "C", "D"])
			self.assertEqual(x.leaf_nodes(), [])
			self.assertEqual(x.root_nodes(), [])
			self.assertEqual(x.child_nodes("A"), ["D"])
			self.assertEqual(x.child_nodes("A", ignore_priority=2), [])
			self.assertEqual(x.parent_nodes("A"), ["B"])
			self.assertEqual(x.parent_nodes("A", ignore_priority=-2), ["B"])
			self.assertEqual(x.parent_nodes("A", ignore_priority=-1), [])
			self.assertEqual(x.hasallzeros(), False)
			self._assertBFSEqual(x.bfs("A"), [(None, "A"), ("A", "D"), ("D", "C"), ("C", "B")])
			self.assertEqual(x.shortest_path("A", "D"), ["A", "D"])
			self.assertEqual(x.shortest_path("D", "A"), ["D", "C", "B", "A"])
			self.assertEqual(x.shortest_path("A", "D", ignore_priority=2), None)
			self.assertEqual(x.shortest_path("D", "A", ignore_priority=-2), ["D", "C", "B", "A"])
			cycles = set(tuple(y) for y in x.get_cycles())
			self.assertEqual(cycles, set([("D", "C", "B", "A"), ("C", "B", "A", "D"), ("B", "A", "D", "C"), \
				("A", "D", "C", "B")]))
			x.remove_edge("A", "B")
			self.assertEqual(x.get_cycles(), [])
			x.difference_update(["D"])
			self.assertEqual(x.all_nodes(), ["A", "B", "C"])
			portage.util.noiselimit = -2
			x.debug_print()
			portage.util.noiselimit = 0

	def testDigraphTree(self):
		g = digraph()
		g.add("B", "A", -1)
		g.add("C", "A", 0)
		g.add("D", "C", 1)
		g.add("E", "C", 2)

		f = g.clone()
		for x in g, f:
			self.assertEqual(bool(x), True)
			self.assertEqual(x.contains("A"), True)
			self.assertEqual(x.firstzero(), "B")
			self.assertRaises(KeyError, x.remove, "Z")
			x.delnode("Z")
			self.assertEqual(set(x), set(["A", "B", "C", "D", "E"]))
			self.assertEqual(x.get("A"), "A")
			self.assertEqual(x.get("A", "default"), "A")
			self.assertEqual(set(x.all_nodes()), set(["A", "B", "C", "D", "E"]))
			self.assertEqual(set(x.leaf_nodes()), set(["B", "D", "E"]))
			self.assertEqual(set(x.leaf_nodes(ignore_priority=0)), set(["A", "B", "D", "E"]))
			self.assertEqual(x.root_nodes(), ["A"])
			self.assertEqual(set(x.root_nodes(ignore_priority=0)), set(["A", "B", "C"]))
			self.assertEqual(set(x.child_nodes("A")), set(["B", "C"]))
			self.assertEqual(x.child_nodes("A", ignore_priority=2), [])
			self.assertEqual(x.parent_nodes("B"), ["A"])
			self.assertEqual(x.parent_nodes("B", ignore_priority=-2), ["A"])
			self.assertEqual(x.parent_nodes("B", ignore_priority=-1), [])
			self.assertEqual(x.hasallzeros(), False)
			self._assertBFSEqual(x.bfs("A"), [(None, "A"), [("A", "C"), ("A", "B")], [("C", "E"), ("C", "D")]])
			self.assertEqual(x.shortest_path("A", "D"), ["A", "C", "D"])
			self.assertEqual(x.shortest_path("D", "A"), None)
			self.assertEqual(x.shortest_path("A", "D", ignore_priority=2), None)
			cycles = set(tuple(y) for y in x.get_cycles())
			self.assertEqual(cycles, set())
			x.remove("D")
			self.assertEqual(set(x.all_nodes()), set(["A", "B", "C", "E"]))
			x.remove("C")
			self.assertEqual(set(x.all_nodes()), set(["A", "B", "E"]))
			portage.util.noiselimit = -2
			x.debug_print()
			portage.util.noiselimit = 0
			self.assertRaises(KeyError, x.remove_edge, "A", "E")

	def testDigraphCompleteGraph(self):
		g = digraph()
		g.add("A", "B", -1)
		g.add("B", "A", 1)
		g.add("A", "C", 1)
		g.add("C", "A", -1)
		g.add("C", "B", 1)
		g.add("B", "C", 1)

		f = g.clone()
		for x in g, f:
			self.assertEqual(bool(x), True)
			self.assertEqual(x.contains("A"), True)
			self.assertEqual(x.firstzero(), None)
			self.assertRaises(KeyError, x.remove, "Z")
			x.delnode("Z")
			self.assertEqual(list(x), ["A", "B", "C"])
			self.assertEqual(x.get("A"), "A")
			self.assertEqual(x.get("A", "default"), "A")
			self.assertEqual(x.all_nodes(), ["A", "B", "C"])
			self.assertEqual(x.leaf_nodes(), [])
			self.assertEqual(x.root_nodes(), [])
			self.assertEqual(set(x.child_nodes("A")), set(["B", "C"]))
			self.assertEqual(x.child_nodes("A", ignore_priority=0), ["B"])
			self.assertEqual(set(x.parent_nodes("A")), set(["B", "C"]))
			self.assertEqual(x.parent_nodes("A", ignore_priority=0), ["C"])
			self.assertEqual(x.parent_nodes("A", ignore_priority=1), [])
			self.assertEqual(x.hasallzeros(), False)
			self._assertBFSEqual(x.bfs("A"), [(None, "A"), [("A", "C"), ("A", "B")]])
			self.assertEqual(x.shortest_path("A", "C"), ["A", "C"])
			self.assertEqual(x.shortest_path("C", "A"), ["C", "A"])
			self.assertEqual(x.shortest_path("A", "C", ignore_priority=0), ["A", "B", "C"])
			self.assertEqual(x.shortest_path("C", "A", ignore_priority=0), ["C", "A"])
			cycles = set(frozenset(y) for y in x.get_cycles())
			self.assertEqual(cycles, set([frozenset(["A", "B"]), frozenset(["A", "C"]), frozenset(["B", "C"])]))
			x.remove_edge("A", "B")
			cycles = set(frozenset(y) for y in x.get_cycles())
			self.assertEqual(cycles, set([frozenset(["A", "C"]), frozenset(["C", "B"])]))
			x.difference_update(["C"])
			self.assertEqual(x.all_nodes(), ["A", "B"])
			portage.util.noiselimit = -2
			x.debug_print()
			portage.util.noiselimit = 0

	def testDigraphIgnorePriority(self):

		def always_true(dummy):
			return True

		def always_false(dummy):
			return False

		g = digraph()
		g.add("A", "B")

		self.assertEqual(g.parent_nodes("A"), ["B"])
		self.assertEqual(g.parent_nodes("A", ignore_priority=always_false), ["B"])
		self.assertEqual(g.parent_nodes("A", ignore_priority=always_true), [])

		self.assertEqual(g.child_nodes("B"), ["A"])
		self.assertEqual(g.child_nodes("B", ignore_priority=always_false), ["A"])
		self.assertEqual(g.child_nodes("B", ignore_priority=always_true), [])

		self.assertEqual(g.leaf_nodes(), ["A"])
		self.assertEqual(g.leaf_nodes(ignore_priority=always_false), ["A"])
		self.assertEqual(g.leaf_nodes(ignore_priority=always_true), ["A", "B"])

		self.assertEqual(g.root_nodes(), ["B"])
		self.assertEqual(g.root_nodes(ignore_priority=always_false), ["B"])
		self.assertEqual(g.root_nodes(ignore_priority=always_true), ["A", "B"])
