def full_analysis(profile, committee):
    """
    Test all implemented properties for the given committee.

    Returns a dictionary with the following keys: "pareto", "jr", "pjr", and "ejr".
    The values are `True` or `False`, depending on whether this property is satisfied.

    Parameters
    ----------
    profile : abcvoting.preferences.Profile
        A profile.
    committee : iterable of int
        A committee.

    Returns
    -------
    dict
    """
    results = {}

    # temporarily no output
    current_verbosity = output.verbosity
    output.set_verbosity(WARNING)

    results["pareto"] = check_pareto_optimality(profile, committee)
    results["jr"] = check_JR(profile, committee)
    results["pjr"] = check_PJR(profile, committee)
    results["ejr"] = check_EJR(profile, committee)

    description = {
        "pareto": "Pareto optimality",
        "jr": "Justified representation (JR)",
        "pjr": "Proportional justified representation (PJR)",
        "ejr": "Extended justified representation (EJR)",
    }

    # restore output verbosity
    output.set_verbosity(current_verbosity)

    for prop, value in results.items():
        output.info(f"{description[prop]:50s} : {value}")
def check_pareto_optimality(profile, committee, algorithm="fastest"):
    """
    Test whether a committee satisfies Pareto optimality.

    Parameters
    ----------
    profile : abcvoting.preferences.Profile
        A profile.
    committee : iterable of int
        A committee.
    algorithm : str, optional
        The algorithm to be used.

    Returns
    -------
    bool

    References
    ----------
    Multi-Winner Voting with Approval Preferences.
    Martin Lackner and Piotr Skowron.
    <https://arxiv.org/abs/2007.01795>

    Examples
    --------
    .. doctest::

        >>> from abcvoting.preferences import Profile
        >>> from abcvoting.output import output, DETAILS

        >>> profile = Profile(3)
        >>> profile.add_voters([[0], [0, 2], [1, 2], [1, 2]])
        >>> print(profile)
        profile with 4 voters and 3 candidates:
         voter 0:   {0},
         voter 1:   {0, 2},
         voter 2:   {1, 2},
         voter 3:   {1, 2}

        >>> output.set_verbosity(DETAILS)  # enable output for check_pareto_optimality
        >>> result = check_pareto_optimality(profile, committee={0, 1})
        Committee {0, 1} is not Pareto optimal.
        (It is dominated by the committee {0, 2}.)
        >>> result = check_pareto_optimality(profile, committee={0, 2})
        Committee {0, 2} is Pareto optimal.

    .. testcleanup::

        output.set_verbosity()

    We see that the committee {0, 2} is Pareto optimal, but not the committee {0, 1}.
    """

    # check that `committee` is a valid input
    committee = CandidateSet(committee, num_cand=profile.num_cand)

    if algorithm == "fastest":
        if gb:
            algorithm = "gurobi"
        else:
            algorithm = "brute-force"

    if algorithm == "brute-force":
        result, detailed_information = _check_pareto_optimality_brute_force(profile, committee)
    elif algorithm == "gurobi":
        result, detailed_information = _check_pareto_optimality_gurobi(profile, committee)
    else:
        raise NotImplementedError(
            "Algorithm " + str(algorithm) + " not specified for check_pareto_optimality"
        )

    if result:
        output.info(f"Committee {str_set_of_candidates(committee)} is Pareto optimal.")
    else:
        output.info(f"Committee {str_set_of_candidates(committee)} is not Pareto optimal.")
        dominating_committee = detailed_information["dominating_committee"]
        output.details(
            f"(It is dominated by the committee {str_set_of_candidates(dominating_committee)}.)"
        )

    return result
def check_JR(profile, committee):
    """
    Test whether a committee satisfies Justified Representation (JR).

    Parameters
    ----------
    profile : abcvoting.preferences.Profile
        A profile.
    committee : iterable of int
        A committee.

    Returns
    -------
    bool

    References
    ----------
    Aziz, H., Brill, M., Conitzer, V., Elkind, E., Freeman, R., & Walsh, T. (2017).
    Justified representation in approval-based committee voting.
    Social Choice and Welfare, 48(2), 461-485.
    https://arxiv.org/abs/1407.8269

    Examples
    --------
    .. doctest::

        >>> from abcvoting.preferences import Profile
        >>> from abcvoting.output import output, DETAILS

        >>> profile = Profile(4)
        >>> profile.add_voters([[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2], [3], [3]])
        >>> print(profile)
        profile with 6 voters and 4 candidates:
         voter 0:   {0, 1, 2},
         voter 1:   {0, 1, 2},
         voter 2:   {0, 1, 2},
         voter 3:   {0, 1, 2},
         voter 4:   {3},
         voter 5:   {3}

        >>> output.set_verbosity(DETAILS)  # enable output for check_JR
        >>> result = check_JR(profile, committee={0, 1, 2})
        Committee {0, 1, 2} does not satisfy JR.
        (The 1-cohesive group of voters {4, 5} (33.3% of all voters) jointly
        approve candidate 3, but none of them approve a candidate in the
        committee.)

        >>> result = check_JR(profile, committee={1, 2, 3})
        Committee {1, 2, 3} satisfies JR.

    .. testcleanup::

        output.set_verbosity()
    """

    # check that `committee` is a valid input
    committee = CandidateSet(committee, num_cand=profile.num_cand)

    result, detailed_information = _check_JR(profile, committee)

    if result:
        output.info(f"Committee {str_set_of_candidates(committee)} satisfies JR.")
    else:
        output.info(f"Committee {str_set_of_candidates(committee)} does not satisfy JR.")
        cand = detailed_information["joint_candidate"]
        cohesive_group = detailed_information["cohesive_group"]
        output.details(
            f"(The 1-cohesive group of voters {str_set_of_candidates(cohesive_group)}"
            f" ({len(cohesive_group)/len(profile)*100:.1f}% of all voters)"
            f" jointly approve candidate {profile.cand_names[cand]}, but none of them "
            "approve a candidate in the committee.)"
        )

    return result
def check_PJR(profile, committee, algorithm="fastest"):
    """
    Test whether a committee satisfies Proportional Justified Representation (PJR).

    Parameters
    ----------
    profile : abcvoting.preferences.Profile
        A profile.
    committee : iterable of int
        A committee.
    algorithm : str, optional
        The algorithm to be used.

    Returns
    -------
    bool

    References
    ----------
    Sánchez-Fernández, L., Elkind, E., Lackner, M., Fernández, N., Fisteus, J., Val, P. B., &
    Skowron, P. (2017).
    Proportional justified representation.
    In Proceedings of the AAAI Conference on Artificial Intelligence (Vol. 31, No. 1).
    https://arxiv.org/abs/1611.09928

    Examples
    --------
    .. doctest::

        >>> from abcvoting.preferences import Profile
        >>> from abcvoting.output import output, DETAILS

        >>> profile = Profile(5)
        >>> profile.add_voters([[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2], [3, 4], [3, 4]])
        >>> print(profile)
        profile with 6 voters and 5 candidates:
         voter 0:   {0, 1, 2},
         voter 1:   {0, 1, 2},
         voter 2:   {0, 1, 2},
         voter 3:   {0, 1, 2},
         voter 4:   {3, 4},
         voter 5:   {3, 4}

        >>> output.set_verbosity(DETAILS)  # enable output for check_PJR
        >>> result = check_PJR(profile, committee={0, 3, 4})
        Committee {0, 3, 4} does not satisfy PJR.
        (The 2-cohesive group of voters {0, 1, 2, 3} (66.7% of all voters)
        jointly approve the candidates {0, 1, 2}, but they approve fewer than
        2 candidates in the committee.)

        >>> result = check_PJR(profile, committee={1, 2, 3})
        Committee {1, 2, 3} satisfies PJR.

    .. testcleanup::

        output.set_verbosity()
    """

    # check that `committee` is a valid input
    committee = CandidateSet(committee, num_cand=profile.num_cand)

    if algorithm == "fastest":
        if gb:
            algorithm = "gurobi"
        else:
            algorithm = "brute-force"

    if algorithm == "brute-force":
        result, detailed_information = _check_PJR_brute_force(profile, committee)
    elif algorithm == "gurobi":
        result, detailed_information = _check_PJR_gurobi(profile, committee)
    else:
        raise NotImplementedError("Algorithm " + str(algorithm) + " not specified for check_PJR")

    if result:
        output.info(f"Committee {str_set_of_candidates(committee)} satisfies PJR.")
    else:
        output.info(f"Committee {str_set_of_candidates(committee)} does not satisfy PJR.")
        ell = detailed_information["ell"]
        cands = detailed_information["joint_candidates"]
        cohesive_group = detailed_information["cohesive_group"]
        output.details(
            f"(The {ell}-cohesive group of voters {str_set_of_candidates(cohesive_group)}"
            f" ({len(cohesive_group)/len(profile)*100:.1f}% of all voters)"
            f" jointly approve the candidates {str_set_of_candidates(cands)}, but they "
            f"approve fewer than {ell} candidates in the committee.)"
        )

    return result
def check_EJR(profile, committee, algorithm="fastest"):
    """
    Test whether a committee satisfies Extended Justified Representation (EJR).

    Parameters
    ----------
    profile : abcvoting.preferences.Profile
        A profile.
    committee : iterable of int
        A committee.
    algorithm : str, optional
        The algorithm to be used.

    Returns
    -------
    bool

    References
    ----------
    Aziz, H., Brill, M., Conitzer, V., Elkind, E., Freeman, R., & Walsh, T. (2017).
    Justified representation in approval-based committee voting.
    Social Choice and Welfare, 48(2), 461-485.
    https://arxiv.org/abs/1407.8269

    Examples
    --------
    .. doctest::

        >>> from abcvoting.preferences import Profile
        >>> from abcvoting.output import output, DETAILS

        >>> profile = Profile(5)
        >>> profile.add_voters([[0, 1, 2], [0, 1, 2], [0, 1, 2], [0, 1, 2], [3, 4], [3, 4]])
        >>> print(profile)
        profile with 6 voters and 5 candidates:
         voter 0:   {0, 1, 2},
         voter 1:   {0, 1, 2},
         voter 2:   {0, 1, 2},
         voter 3:   {0, 1, 2},
         voter 4:   {3, 4},
         voter 5:   {3, 4}

        >>> output.set_verbosity(DETAILS)  # enable output for check_EJR
        >>> result = check_EJR(profile, committee={0, 3, 4})
        Committee {0, 3, 4} does not satisfy EJR.
         (The 2-cohesive group of voters {0, 1, 2, 3} (66.7% of all voters)
         jointly approve the candidates {0, 1, 2}, but none of them approves 2
         candidates in the committee.)

        >>> result = check_EJR(profile, committee={1, 2, 3})
        Committee {1, 2, 3} satisfies EJR.

    .. testcleanup::

        output.set_verbosity()
    """

    # check that `committee` is a valid input
    committee = CandidateSet(committee, num_cand=profile.num_cand)

    if algorithm == "fastest":
        if gb:
            algorithm = "gurobi"
        else:
            algorithm = "brute-force"

    if algorithm == "brute-force":
        result, detailed_information = _check_EJR_brute_force(profile, committee)
    elif algorithm == "gurobi":
        result, detailed_information = _check_EJR_gurobi(profile, committee)
    else:
        raise NotImplementedError("Algorithm " + str(algorithm) + " not specified for check_EJR")

    if result:
        output.info(f"Committee {str_set_of_candidates(committee)} satisfies EJR.")
    else:
        output.info(f"Committee {str_set_of_candidates(committee)} does not satisfy EJR.")
        ell = detailed_information["ell"]
        cands = detailed_information["joint_candidates"]
        cohesive_group = detailed_information["cohesive_group"]
        output.details(
            f"(The {ell}-cohesive group of voters {str_set_of_candidates(cohesive_group)}"
            f" ({len(cohesive_group)/len(profile)*100:.1f}% of all voters)"
            f" jointly approve the candidates {str_set_of_candidates(cands)}, but none of them "
            f"approves {ell} candidates in the committee.)",
            indent=" ",
        )

    return result