# Path: simpleai/search/arc.py
# def all_arcs(constraints):
#     """
#     For each constraint ((X, Y), const) adds:
#         ((X, Y), const)
#         ((Y, X), const)
#     """
#     arcs = set()
# 
#     for neighbors, constraint in constraints:
#         if len(neighbors) == 2:
#             x, y = neighbors
#             list(map(arcs.add, ((x, y), (y, x))))
# 
#     return arcs
# 
# def revise(domains, arc, constraints):
#     """
#     Given the arc X, Y (variables), removes the values from X's domain that
#     do not meet the constraint between X and Y.
# 
#     That is, given x1 in X's domain, x1 will be removed from the domain, if
#     there is no value y in Y's domain that makes constraint(X,Y) True, for
#     those constraints affecting X and Y.
#     """
#     x, y = arc
#     related_constraints = [(neighbors, constraint)
#                            for neighbors, constraint in constraints
#                            if set(arc) == set(neighbors)]
# 
#     modified = False
# 
#     for neighbors, constraint in related_constraints:
#         for x_value in domains[x]:
#             constraint_results = (_call_constraint({x: x_value, y: y_value},
#                                                    neighbors, constraint)
#                                   for y_value in domains[y])
# 
#             if not any(constraint_results):
#                 domains[x].remove(x_value)
#                 modified = True
# 
#     return modified
# 
# def arc_consistency_3(domains, constraints):
#     """
#     Makes a CSP problem arc consistent.
# 
#     Ignores any constraint that is not binary.
#     """
#     arcs = list(all_arcs(constraints))
#     pending_arcs = set(arcs)
# 
#     while pending_arcs:
#         x, y = pending_arcs.pop()
#         if revise(domains, (x, y), constraints):
#             if len(domains[x]) == 0:
#                 return False
#             pending_arcs = pending_arcs.union((x2, y2) for x2, y2 in arcs
#                                               if y2 == x)
#     return True


import unittest
from operator import itemgetter
from simpleai.search.arc import all_arcs, revise, arc_consistency_3

        self.assertEqual(arcs_result, arcs_expected)


def is_square(variables, values):
    return values[0] ** 2 == values[1]


class TestReviseDomain(unittest.TestCase):
    def revise(self, domain_a, domain_b, duplicate_constraints=False):
        domains = {'A': domain_a, 'B': domain_b}
        constraints = [(('A', 'B'), is_square)]
        if duplicate_constraints:
            constraints = constraints * 2

        return revise(domains, ('A', 'B'), constraints), domains

    def test_if_all_values_have_possible_match_the_domain_is_untouched(self):
        result, domains = self.revise([1, 2, 3], [1, 4, 9])
        self.assertFalse(result)
        self.assertEqual(domains['A'], [1, 2, 3])

    def test_if_a_value_has_no_possible_match_remove_it_from_domain(self):
        result, domains = self.revise([1, 2, 3], [1, 4])
        self.assertTrue(result)
        self.assertEqual(domains['A'], [1, 2])

    def test_if_multiple_constraints_dont_fail_removing_twice(self):
        # there was a bug when two constraints tried to remove the same value
        result, domains = self.revise([1, 2, 3], [1, 4], True)

# Path: simpleai/search/arc.py
# def all_arcs(constraints):
#     """
#     For each constraint ((X, Y), const) adds:
#         ((X, Y), const)
#         ((Y, X), const)
#     """
#     arcs = set()
# 
#     for neighbors, constraint in constraints:
#         if len(neighbors) == 2:
#             x, y = neighbors
#             list(map(arcs.add, ((x, y), (y, x))))
# 
#     return arcs
# 
# def revise(domains, arc, constraints):
#     """
#     Given the arc X, Y (variables), removes the values from X's domain that
#     do not meet the constraint between X and Y.
# 
#     That is, given x1 in X's domain, x1 will be removed from the domain, if
#     there is no value y in Y's domain that makes constraint(X,Y) True, for
#     those constraints affecting X and Y.
#     """
#     x, y = arc
#     related_constraints = [(neighbors, constraint)
#                            for neighbors, constraint in constraints
#                            if set(arc) == set(neighbors)]
# 
#     modified = False
# 
#     for neighbors, constraint in related_constraints:
#         for x_value in domains[x]:
#             constraint_results = (_call_constraint({x: x_value, y: y_value},
#                                                    neighbors, constraint)
#                                   for y_value in domains[y])
# 
#             if not any(constraint_results):
#                 domains[x].remove(x_value)
#                 modified = True
# 
#     return modified
# 
# def arc_consistency_3(domains, constraints):
#     """
#     Makes a CSP problem arc consistent.
# 
#     Ignores any constraint that is not binary.
#     """
#     arcs = list(all_arcs(constraints))
#     pending_arcs = set(arcs)
# 
#     while pending_arcs:
#         x, y = pending_arcs.pop()
#         if revise(domains, (x, y), constraints):
#             if len(domains[x]) == 0:
#                 return False
#             pending_arcs = pending_arcs.union((x2, y2) for x2, y2 in arcs
#                                               if y2 == x)
#     return True


# Path: tests/search/test_arc_concistency.py
import unittest
from operator import itemgetter
from simpleai.search.arc import all_arcs, revise, arc_consistency_3


        self.assertEqual(arcs_result, arcs_expected)


def is_square(variables, values):
    return values[0] ** 2 == values[1]


class TestReviseDomain(unittest.TestCase):
    def revise(self, domain_a, domain_b, duplicate_constraints=False):
        domains = {'A': domain_a, 'B': domain_b}
        constraints = [(('A', 'B'), is_square)]
        if duplicate_constraints:
            constraints = constraints * 2

        return revise(domains, ('A', 'B'), constraints), domains

    def test_if_all_values_have_possible_match_the_domain_is_untouched(self):
        result, domains = self.revise([1, 2, 3], [1, 4, 9])
        self.assertFalse(result)
        self.assertEqual(domains['A'], [1, 2, 3])

    def test_if_a_value_has_no_possible_match_remove_it_from_domain(self):
        result, domains = self.revise([1, 2, 3], [1, 4])
        self.assertTrue(result)
        self.assertEqual(domains['A'], [1, 2])

    def test_if_multiple_constraints_dont_fail_removing_twice(self):
        # there was a bug when two constraints tried to remove the same value
        result, domains = self.revise([1, 2, 3], [1, 4], True)

        self.assertTrue(result)