# Path: lcs/agents/macs/macs.py
# class ClassifiersList(TypedList[Classifier]):
#     def __init__(self, *args, oktypes=(Classifier,)) -> None:
#         super().__init__(*args, oktypes=oktypes)
# 
#     def form_match_set(self, situation: Perception) -> ClassifiersList:
#         matching = [cl for cl in self if cl.does_match(situation)]
#         return ClassifiersList(*matching)
# 
#     def form_action_set(self, action: int) -> ClassifiersList:
#         matching = [cl for cl in self if cl.action == action]
#         return ClassifiersList(*matching)
# 
# class Configuration:
#     def __init__(self,
#                  classifier_length: int,
#                  number_of_possible_actions: int,
#                  feature_possible_values: list,
#                  specified_effect_attributes: int = 1,
#                  estimate_expected_improvements: bool = True,
#                  learning_rate: float = 0.1,
#                  inaccuracy_threshold: int = 5,
#                  accuracy_threshold: int = 5,
#                  oscillation_threshold: int = 5,
#                  metrics_trial_frequency: int = 5,
#                  user_metrics_collector_fcn: Callable = None):
# 
#         assert classifier_length == len(feature_possible_values)
#         assert 1 <= specified_effect_attributes <= classifier_length
# 
#         self.classifier_length = classifier_length
#         self.number_of_possible_actions = number_of_possible_actions
#         self.feature_possible_values = feature_possible_values
#         self.specified_effect_attributes = specified_effect_attributes
#         self.estimate_expected_improvements = estimate_expected_improvements
#         self.beta = learning_rate
#         self.er = inaccuracy_threshold
#         self.ea = accuracy_threshold
#         self.eo = oscillation_threshold
#         self.metrics_trial_frequency = metrics_trial_frequency
#         self.user_metrics_collector_fcn = user_metrics_collector_fcn
# 
#         self.model_checkpoint_freq = False
# 
# class Classifier:
#     def __init__(self,
#                  condition: Union[Condition, str, None] = None,
#                  action: Optional[int] = None,
#                  effect: Union[Effect, str, None] = None,
#                  debug: dict = dict(),
#                  cfg: Optional[Configuration] = None):
# 
#         if cfg is None:
#             raise TypeError("Configuration should be passed to Classifier")
# 
#         self.cfg = cfg
#         self.debug = debug
# 
#         def build_perception_string(cls, initial,
#                                     length=self.cfg.classifier_length):
#             if initial:
#                 return cls(initial)
# 
#             return cls.empty(length=length)
# 
#         self.condition = build_perception_string(Condition, condition)
#         self.action = action
#         self.effect = build_perception_string(Effect, effect)
# 
#         # Number of good anticipations
#         self.g = 0
# 
#         # Situation preceding last anticipation success
#         self.sg: Optional[Perception] = None
# 
#         # Number of bad anticipations
#         self.b = 0
# 
#         # Situation preceding last anticipation mistake
#         self.sb: Optional[Perception] = None
# 
#     def __key(self):
#         return str(self.condition) + str(self.action) + str(self.effect)
# 
#     def __eq__(self, other):
#         return self.__key() == other.__key()
# 
#     def __hash__(self):
#         return hash(self.__key())
# 
#     def __repr__(self):
#         return f"{self.condition}-{self.action}-{self.effect}, G: {self.g}, B: {self.b}, {self.debug}"
# 
#     @property
#     def is_accurate(self) -> bool:
#         return self.b == 0 and self.g >= self.cfg.ea
# 
#     @property
#     def is_inaccurate(self) -> bool:
#         return self.g == 0 and self.b >= self.cfg.er
# 
#     @property
#     def is_oscillating(self) -> bool:
#         return self.g + self.b > self.cfg.eo and self.g * self.b > 0
# 
#     def does_match(self, situation: Perception) -> bool:
#         return self.condition.does_match(situation)
# 
#     def anticipates(self, situation: Perception) -> bool:
#         return self.effect.does_match(situation)
# 
#     def conflicts(self, other: Classifier) -> bool:
#         if self.action == other.action:
#             if self.condition.does_match(other.condition):
#                 if self.effect.conflicts(other.effect):
#                     return True
#         return False


import pytest
from lcs.agents.macs.macs import ClassifiersList, Configuration, Classifier



class TestClassifiersList:

    @pytest.fixture
    def cfg(self):
        feature_vals = {'0', '1'}
        return Configuration(2, 2, feature_possible_values=[feature_vals] * 2)

    def test_should_initialize(self, cfg):
        cls = ClassifiersList()
        assert len(cls) == 0


# Path: lcs/agents/macs/macs.py
# class ClassifiersList(TypedList[Classifier]):
#     def __init__(self, *args, oktypes=(Classifier,)) -> None:
#         super().__init__(*args, oktypes=oktypes)
# 
#     def form_match_set(self, situation: Perception) -> ClassifiersList:
#         matching = [cl for cl in self if cl.does_match(situation)]
#         return ClassifiersList(*matching)
# 
#     def form_action_set(self, action: int) -> ClassifiersList:
#         matching = [cl for cl in self if cl.action == action]
#         return ClassifiersList(*matching)
# 
# class Configuration:
#     def __init__(self,
#                  classifier_length: int,
#                  number_of_possible_actions: int,
#                  feature_possible_values: list,
#                  specified_effect_attributes: int = 1,
#                  estimate_expected_improvements: bool = True,
#                  learning_rate: float = 0.1,
#                  inaccuracy_threshold: int = 5,
#                  accuracy_threshold: int = 5,
#                  oscillation_threshold: int = 5,
#                  metrics_trial_frequency: int = 5,
#                  user_metrics_collector_fcn: Callable = None):
# 
#         assert classifier_length == len(feature_possible_values)
#         assert 1 <= specified_effect_attributes <= classifier_length
# 
#         self.classifier_length = classifier_length
#         self.number_of_possible_actions = number_of_possible_actions
#         self.feature_possible_values = feature_possible_values
#         self.specified_effect_attributes = specified_effect_attributes
#         self.estimate_expected_improvements = estimate_expected_improvements
#         self.beta = learning_rate
#         self.er = inaccuracy_threshold
#         self.ea = accuracy_threshold
#         self.eo = oscillation_threshold
#         self.metrics_trial_frequency = metrics_trial_frequency
#         self.user_metrics_collector_fcn = user_metrics_collector_fcn
# 
#         self.model_checkpoint_freq = False
# 
# class Classifier:
#     def __init__(self,
#                  condition: Union[Condition, str, None] = None,
#                  action: Optional[int] = None,
#                  effect: Union[Effect, str, None] = None,
#                  debug: dict = dict(),
#                  cfg: Optional[Configuration] = None):
# 
#         if cfg is None:
#             raise TypeError("Configuration should be passed to Classifier")
# 
#         self.cfg = cfg
#         self.debug = debug
# 
#         def build_perception_string(cls, initial,
#                                     length=self.cfg.classifier_length):
#             if initial:
#                 return cls(initial)
# 
#             return cls.empty(length=length)
# 
#         self.condition = build_perception_string(Condition, condition)
#         self.action = action
#         self.effect = build_perception_string(Effect, effect)
# 
#         # Number of good anticipations
#         self.g = 0
# 
#         # Situation preceding last anticipation success
#         self.sg: Optional[Perception] = None
# 
#         # Number of bad anticipations
#         self.b = 0
# 
#         # Situation preceding last anticipation mistake
#         self.sb: Optional[Perception] = None
# 
#     def __key(self):
#         return str(self.condition) + str(self.action) + str(self.effect)
# 
#     def __eq__(self, other):
#         return self.__key() == other.__key()
# 
#     def __hash__(self):
#         return hash(self.__key())
# 
#     def __repr__(self):
#         return f"{self.condition}-{self.action}-{self.effect}, G: {self.g}, B: {self.b}, {self.debug}"
# 
#     @property
#     def is_accurate(self) -> bool:
#         return self.b == 0 and self.g >= self.cfg.ea
# 
#     @property
#     def is_inaccurate(self) -> bool:
#         return self.g == 0 and self.b >= self.cfg.er
# 
#     @property
#     def is_oscillating(self) -> bool:
#         return self.g + self.b > self.cfg.eo and self.g * self.b > 0
# 
#     def does_match(self, situation: Perception) -> bool:
#         return self.condition.does_match(situation)
# 
#     def anticipates(self, situation: Perception) -> bool:
#         return self.effect.does_match(situation)
# 
#     def conflicts(self, other: Classifier) -> bool:
#         if self.action == other.action:
#             if self.condition.does_match(other.condition):
#                 if self.effect.conflicts(other.effect):
#                     return True
#         return False


# Path: tests/lcs/agents/macs/test_ClassifiersList.py
import pytest
from lcs.agents.macs.macs import ClassifiersList, Configuration, Classifier




class TestClassifiersList:

    @pytest.fixture
    def cfg(self):
        feature_vals = {'0', '1'}
        return Configuration(2, 2, feature_possible_values=[feature_vals] * 2)

    def test_should_initialize(self, cfg):
        cls = ClassifiersList()
        assert len(cls) == 0


        cls = ClassifiersList(*[