# Unit tests


import unittest


def run_test_instance(unittestinstance, profile, committeesize, tests):
    import rules_approval

    # all rules used?
    for rule in rules_approval.MWRULES:
        unittestinstance.assertTrue(rule in tests.keys())

    for rule in tests.keys():

        output = rules_approval.compute_rule(rule, profile,
                                             committeesize,
                                             resolute=False)
        unittestinstance.assertEqual(
            output, tests[rule], msg=rules_approval.MWRULES[rule] + " failed")
        output = rules_approval.compute_rule(
            rule, profile, committeesize, resolute=True)
        unittestinstance.assertEqual(
            len(output), 1,
            msg=rules_approval.MWRULES[rule] + " failed with resolute=True")
        unittestinstance.assertTrue(
            output[0] in tests[rule],
            msg=rules_approval.MWRULES[rule] + " failed with resolute=True")


class TestApprovalMultiwinner(unittest.TestCase):
    def test_createprofiles(self):
        from preferences import Profile
        from preferences import DichotomousPreferences
        num_cand = 7
        prof = Profile(num_cand)
        self.assertEqual(prof.add_preferences(
            DichotomousPreferences([0, 4, 5])),
            None)
        with self.assertRaises(Exception):
            prof.add_preferences(DichotomousPreferences([num_cand]))
        with self.assertRaises(Exception):
            prof.add_preferences(DichotomousPreferences([-1]))
        self.assertEqual(prof.add_preferences([0, 4, 5]), None)
        with self.assertRaises(Exception):
            prof.add_preferences([0, 4, 5, "1"])
        with self.assertRaises(Exception):
            prof.add_preferences(["1", 0, 4, 5])
        p1 = DichotomousPreferences([0, 4, 5])
        p2 = DichotomousPreferences([1, 2])
        self.assertEqual(prof.add_preferences([p1, p2]), None)
        self.assertTrue(prof.has_unit_weights())
        prof.add_preferences(DichotomousPreferences([0, 4, 5], 2.4))
        self.assertFalse(prof.has_unit_weights())
        self.assertEqual(prof.totalweight(), 6.4)

    def test_mwrules__toofewcandidates(self):
        from preferences import Profile
        import rules_approval
        profile = Profile(5)
        committeesize = 4
        preflist = [[0, 1, 2], [1], [1, 2], [0]]
        profile.add_preferences(preflist)

        for rule in rules_approval.MWRULES.keys():
            with self.assertRaises(Exception):
                rules_approval.compute_rule(rule, profile, committeesize)
            with self.assertRaises(Exception):
                rules_approval.compute_rule(rule, profile,
                                            committeesize, resolute=True)

    def test_mwrules_weightsconsidered(self):
        from preferences import Profile
        from preferences import DichotomousPreferences
        import rules_approval

        self.longMessage = True

        profile = Profile(3)
        profile.add_preferences(DichotomousPreferences([0]))
        profile.add_preferences(DichotomousPreferences([0]))
        profile.add_preferences(DichotomousPreferences([1], 5))
        profile.add_preferences(DichotomousPreferences([0]))
        committeesize = 1

        for rule in rules_approval.MWRULES.keys():
            if "monroe" in rule or "rule-x" in rule:
                # Monroe and rule x only work with unit weights:
                continue
            result = rules_approval.compute_rule(rule, profile, committeesize)
            self.assertTrue([1] in result,
                            msg=rule + " failed"+str(result))

    def test_mwrules_correct_simple(self):
        from preferences import Profile
        import rules_approval

        self.longMessage = True

        profile = Profile(4)
        profile.add_preferences([[0], [1], [2], [3]])
        committeesize = 2

        for rule in rules_approval.MWRULES.keys():
            if rule == "greedy-monroe":   # always returns one committee
                continue
            self.assertEqual(len(rules_approval.compute_rule(rule, profile,
                                                             committeesize)),
                             6, msg=rule + " failed")

        for rule in rules_approval.MWRULES.keys():
            self.assertEqual(len(rules_approval.compute_rule(rule, profile,
                                                             committeesize,
                                                             resolute=True)),
                             1, msg=rule + " failed with resolute=True")

    def test_monroe_indivisible(self):
        from preferences import Profile
        import rules_approval

        self.longMessage = True

        profile = Profile(4)
        profile.add_preferences([[0], [0], [0], [1, 2], [1, 2], [1], [3]])
        committeesize = 3

        for ilp in [True, False]:
            # max Monroe score is 6 (even for committee [0, 1, 3])
            self.assertEqual(
                rules_approval.compute_monroe(profile, committeesize,
                                              ilp=ilp, resolute=False),
                [[0, 1, 2], [0, 1, 3], [0, 2, 3]])

    # this test shows that tiebreaking is not (yet)
    # implemented for opt-Phragmen
    def test_optphrag_notiebreaking(self):
        from preferences import Profile
        from rules_approval import compute_rule

        self.longMessage = True

        profile = Profile(6)
        profile.add_preferences([[0], [0], [1, 3], [1, 3], [1, 4],
                                 [2, 4], [2, 5], [2, 5]])
        committeesize = 3

        self.assertEqual(
                len(compute_rule("optphrag", profile, committeesize,
                                 resolute=False)),
                12)

    def test_mwrules_correct_advanced_1(self):

        from preferences import Profile
        self.longMessage = True
        committeesize = 4

        profile = Profile(6)
        preflist = [[0, 4, 5], [0], [1, 4, 5], [1],
                    [2, 4, 5], [2], [3, 4, 5], [3]]
        profile.add_preferences(preflist)

        tests1 = {
            "seqpav": [[0, 1, 4, 5], [0, 2, 4, 5], [0, 3, 4, 5],
                       [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]],
            "av": [[0, 1, 4, 5], [0, 2, 4, 5], [0, 3, 4, 5],
                   [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]],
            "sav": [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 2, 5], [0, 1, 3, 4],
                    [0, 1, 3, 5], [0, 1, 4, 5], [0, 2, 3, 4], [0, 2, 3, 5],
                    [0, 2, 4, 5], [0, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 5],
                    [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]],
            "pav-ilp": [[0, 1, 4, 5], [0, 2, 4, 5], [0, 3, 4, 5],
                        [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]],
            "pav-noilp": [[0, 1, 4, 5], [0, 2, 4, 5], [0, 3, 4, 5],
                          [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]],
            "revseqpav": [[0, 1, 4, 5], [0, 2, 4, 5], [0, 3, 4, 5],
                          [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]],
            "minimaxav-noilp": [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 2, 5],
                                [0, 1, 3, 4], [0, 1, 3, 5], [0, 1, 4, 5],
                                [0, 2, 3, 4], [0, 2, 3, 5], [0, 2, 4, 5],
                                [0, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 5],
                                [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]],
            "minimaxav-ilp": [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 2, 5],
                              [0, 1, 3, 4], [0, 1, 3, 5], [0, 1, 4, 5],
                              [0, 2, 3, 4], [0, 2, 3, 5], [0, 2, 4, 5],
                              [0, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 5],
                              [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]],
            "phrag": [[0, 1, 4, 5], [0, 2, 4, 5], [0, 3, 4, 5],
                      [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]],
            "optphrag": [[0, 1, 2, 3]],
            "cc-ilp": [[0, 1, 2, 3]],
            "cc-noilp": [[0, 1, 2, 3]],
            "seqcc": [[0, 1, 2, 4], [0, 1, 2, 5], [0, 1, 3, 4], [0, 1, 3, 5],
                      [0, 2, 3, 4], [0, 2, 3, 5], [1, 2, 3, 4], [1, 2, 3, 5]],
            "revseqcc": [[0, 1, 2, 3]],
            "monroe-ilp": [[0, 1, 2, 3]],
            "monroe-noilp": [[0, 1, 2, 3]],
            "greedy-monroe": [[0, 2, 3, 4]],
            "slav-ilp": [[0, 1, 2, 3],
                         [0, 1, 2, 4], [0, 1, 2, 5],
                         [0, 1, 3, 4], [0, 1, 3, 5],
                         [0, 2, 3, 4], [0, 2, 3, 5],
                         [1, 2, 3, 4], [1, 2, 3, 5]],
            "slav-noilp": [[0, 1, 2, 3],
                           [0, 1, 2, 4], [0, 1, 2, 5],
                           [0, 1, 3, 4], [0, 1, 3, 5],
                           [0, 2, 3, 4], [0, 2, 3, 5],
                           [1, 2, 3, 4], [1, 2, 3, 5]],
            "seqslav": [[0, 1, 2, 4], [0, 1, 2, 5],
                        [0, 1, 3, 4], [0, 1, 3, 5],
                        [0, 2, 3, 4], [0, 2, 3, 5],
                        [1, 2, 3, 4], [1, 2, 3, 5]],
            "rule-x": [[0, 1, 4, 5], [0, 2, 4, 5],
                       [0, 3, 4, 5], [1, 2, 4, 5],
                       [1, 3, 4, 5], [2, 3, 4, 5]],
            "phragmen-enestroem": [[0, 1, 4, 5], [0, 2, 4, 5],
                                   [0, 3, 4, 5], [1, 2, 4, 5],
                                   [1, 3, 4, 5], [2, 3, 4, 5]],
        }

        run_test_instance(self, profile, committeesize, tests1)

        # and now with reversed preflist
        preflist.reverse()
        for p in preflist:
            p.reverse()
        profile = Profile(6)
        profile.add_preferences(preflist)

        run_test_instance(self, profile, committeesize, tests1)

    def test_mwrules_correct_advanced_2(self):

        from preferences import Profile
        self.longMessage = True

        # and another profile
        profile = Profile(5)
        committeesize = 3
        preflist = [[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2],
                    [0, 1, 2], [0, 1], [3, 4], [3, 4], [3]]
        profile.add_preferences(preflist)

        tests2 = {
            "seqpav": [[0, 1, 3]],
            "av": [[0, 1, 2]],
            "sav": [[0, 1, 3]],
            "pav-ilp": [[0, 1, 3]],
            "pav-noilp": [[0, 1, 3]],
            "revseqpav": [[0, 1, 3]],
            "minimaxav-noilp": [[0, 1, 3], [0, 2, 3], [1, 2, 3]],
            "minimaxav-ilp": [[0, 1, 3], [0, 2, 3], [1, 2, 3]],
            "phrag": [[0, 1, 3]],
            "optphrag": [[0, 1, 3], [0, 2, 3], [1, 2, 3]],
            "cc-ilp": [[0, 1, 3], [0, 2, 3], [0, 3, 4],
                       [1, 2, 3], [1, 3, 4]],
            "cc-noilp": [[0, 1, 3], [0, 2, 3], [0, 3, 4],
                         [1, 2, 3], [1, 3, 4]],
            "seqcc": [[0, 1, 3], [0, 2, 3], [0, 3, 4],
                      [1, 2, 3], [1, 3, 4]],
            "revseqcc": [[0, 1, 3], [0, 2, 3], [0, 3, 4],
                         [1, 2, 3], [1, 3, 4]],
            "monroe-ilp": [[0, 1, 3], [0, 2, 3], [1, 2, 3]],
            "monroe-noilp": [[0, 1, 3], [0, 2, 3], [1, 2, 3]],
            "greedy-monroe": [[0, 1, 3]],
            "seqslav": [[0, 1, 3]],
            "slav-ilp": [[0, 1, 3]],
            "slav-noilp": [[0, 1, 3]],
            "rule-x": [[0, 1, 3]],
            "phragmen-enestroem": [[0, 1, 3]],
        }

        run_test_instance(self, profile, committeesize, tests2)

    def test_mwrules_correct_advanced_3(self):

        from preferences import Profile
        self.longMessage = True

        # and a third profile
        profile = Profile(6)
        committeesize = 4
        preflist = [[0, 3, 4, 5], [1, 2], [0, 2, 5], [2],
                    [0, 1, 2, 3, 4], [0, 3, 4], [0, 2, 4], [0, 1]]
        profile.add_preferences(preflist)

        tests3 = {
            "seqpav": [[0, 1, 2, 4]],
            "av": [[0, 1, 2, 4], [0, 2, 3, 4]],
            "sav": [[0, 1, 2, 4]],
            "pav-ilp": [[0, 1, 2, 4]],
            "pav-noilp": [[0, 1, 2, 4]],
            "revseqpav": [[0, 1, 2, 4]],
            "minimaxav-noilp": [[0, 1, 2, 3], [0, 1, 2, 4],
                                [0, 2, 3, 4], [0, 2, 3, 5],
                                [0, 2, 4, 5]],
            "minimaxav-ilp": [[0, 1, 2, 3], [0, 1, 2, 4],
                              [0, 2, 3, 4], [0, 2, 3, 5],
                              [0, 2, 4, 5]],
            "phrag": [[0, 1, 2, 4]],
            "optphrag": [[0, 1, 2, 3], [0, 1, 2, 4],
                         [0, 1, 2, 5], [0, 2, 3, 4],
                         [0, 2, 3, 5], [0, 2, 4, 5],
                         [1, 2, 3, 4], [1, 2, 3, 5],
                         [1, 2, 4, 5]],
            "cc-ilp": [[0, 1, 2, 3], [0, 1, 2, 4],
                       [0, 1, 2, 5], [0, 2, 3, 4],
                       [0, 2, 3, 5], [0, 2, 4, 5],
                       [1, 2, 3, 4], [1, 2, 3, 5],
                       [1, 2, 4, 5]],
            "cc-noilp": [[0, 1, 2, 3], [0, 1, 2, 4],
                         [0, 1, 2, 5], [0, 2, 3, 4],
                         [0, 2, 3, 5], [0, 2, 4, 5],
                         [1, 2, 3, 4], [1, 2, 3, 5],
                         [1, 2, 4, 5]],
            "seqcc": [[0, 1, 2, 3], [0, 1, 2, 4],
                      [0, 1, 2, 5], [0, 2, 3, 4],
                      [0, 2, 3, 5], [0, 2, 4, 5]],
            "revseqcc": [[0, 1, 2, 3], [0, 1, 2, 4],
                         [0, 1, 2, 5], [0, 2, 3, 4],
                         [0, 2, 3, 5], [0, 2, 4, 5],
                         [1, 2, 3, 4], [1, 2, 3, 5],
                         [1, 2, 4, 5]],
            "monroe-ilp": [[0, 1, 2, 3], [0, 1, 2, 4],
                           [0, 1, 2, 5], [0, 2, 3, 4],
                           [0, 2, 3, 5], [0, 2, 4, 5],
                           [1, 2, 3, 4], [1, 2, 3, 5],
                           [1, 2, 4, 5]],
            "monroe-noilp": [[0, 1, 2, 3], [0, 1, 2, 4],
                             [0, 1, 2, 5], [0, 2, 3, 4],
                             [0, 2, 3, 5], [0, 2, 4, 5],
                             [1, 2, 3, 4], [1, 2, 3, 5],
                             [1, 2, 4, 5]],
            "greedy-monroe": [[0, 1, 2, 3]],
            "seqslav": [[0, 1, 2, 4]],
            "slav-ilp": [[0, 1, 2, 4]],
            "slav-noilp": [[0, 1, 2, 4]],
            "rule-x": [[0, 1, 2, 4]],
            "phragmen-enestroem": [[0, 1, 2, 4]],
        }

        run_test_instance(self, profile, committeesize, tests3)

    def test_monroescore(self):
        from preferences import Profile
        from score_functions import monroescore_flowbased, monroescore_matching
        self.longMessage = True

        # and a third profile
        profile = Profile(6)
        preflist = [[0, 1], [1], [1, 3], [4], [2], [1, 5, 3]]
        profile.add_preferences(preflist)

        self.assertEqual(monroescore_flowbased(profile, [1, 3, 2]), 5)
        self.assertEqual(monroescore_matching(profile, [1, 3, 2]), 5)
        self.assertEqual(monroescore_flowbased(profile, [2, 1, 5]), 4)
        self.assertEqual(monroescore_matching(profile, [2, 1, 5]), 4)
        self.assertEqual(monroescore_flowbased(profile, [2, 4, 5]), 3)
        self.assertEqual(monroescore_matching(profile, [2, 5, 4]), 3)


if __name__ == '__main__':
    unittest.main()

[(363, 'unittest.main', 'unittest.main', False, 'import unittest\n'), (16, 'rules_approval.compute_rule', 'rules_approval.compute_rule', False, 'import rules_approval\n'), (21, 'rules_approval.compute_rule', 'rules_approval.compute_rule', False, 'import rules_approval\n'), (36, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (49, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (50, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (60, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (65, 'rules_approval.MWRULES.keys', 'rules_approval.MWRULES.keys', False, 'import rules_approval\n'), (79, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (86, 'rules_approval.MWRULES.keys', 'rules_approval.MWRULES.keys', False, 'import rules_approval\n'), (100, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (104, 'rules_approval.MWRULES.keys', 'rules_approval.MWRULES.keys', False, 'import rules_approval\n'), (111, 'rules_approval.MWRULES.keys', 'rules_approval.MWRULES.keys', False, 'import rules_approval\n'), (123, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (142, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (158, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (227, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (238, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (281, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (350, 'preferences.Profile', 'Profile', False, 'from preferences import Profile\n'), (53, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (80, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (81, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (82, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (83, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (90, 'rules_approval.compute_rule', 'rules_approval.compute_rule', False, 'import rules_approval\n'), (354, 'score_functions.monroescore_flowbased', 'monroescore_flowbased', False, 'from score_functions import monroescore_flowbased, monroescore_matching\n'), (355, 'score_functions.monroescore_matching', 'monroescore_matching', False, 'from score_functions import monroescore_flowbased, monroescore_matching\n'), (356, 'score_functions.monroescore_flowbased', 'monroescore_flowbased', False, 'from score_functions import monroescore_flowbased, monroescore_matching\n'), (357, 'score_functions.monroescore_matching', 'monroescore_matching', False, 'from score_functions import monroescore_flowbased, monroescore_matching\n'), (358, 'score_functions.monroescore_flowbased', 'monroescore_flowbased', False, 'from score_functions import monroescore_flowbased, monroescore_matching\n'), (359, 'score_functions.monroescore_matching', 'monroescore_matching', False, 'from score_functions import monroescore_flowbased, monroescore_matching\n'), (38, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (41, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (43, 'preferences.DichotomousPreferences', 'DichotomousPreferences', False, 'from preferences import DichotomousPreferences\n'), (67, 'rules_approval.compute_rule', 'rules_approval.compute_rule', False, 'import rules_approval\n'), (69, 'rules_approval.compute_rule', 'rules_approval.compute_rule', False, 'import rules_approval\n'), (130, 'rules_approval.compute_monroe', 'rules_approval.compute_monroe', False, 'import rules_approval\n'), (148, 'rules_approval.compute_rule', 'compute_rule', False, 'from rules_approval import compute_rule\n'), (107, 'rules_approval.compute_rule', 'rules_approval.compute_rule', False, 'import rules_approval\n'), (112, 'rules_approval.compute_rule', 'rules_approval.compute_rule', False, 'import rules_approval\n')]