We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ValueError: list.remove(x): x not in list
### CloudFormation Lint Version

0.76.0

### What operating system are you using?

macOS

### Describe the bug

We run latest `cfn-lint` on all templates in the [AWS SAM repository](https://github.com/aws/serverless-application-model/); looks like 0.76.0 introduced a bug.

Since today, `cfn-lint` fails with:

```text
Traceback (most recent call last):
  File "/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/bin/cfn-lint", line 8, in <module>
    sys.exit(main())
  File "/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/__main__.py", line 39, in main
    matches = list(cfnlint.core.get_matches(filenames, args))
  File "/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/core.py", line 173, in get_matches
    matches = run_cli(
  File "/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/core.py", line 78, in run_cli
    return run_checks(filename, template, rules, regions, mandatory_rules)
  File "/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/core.py", line 334, in run_checks
    runner = cfnlint.runner.Runner(
  File "/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/runner.py", line 32, in __init__
    self.cfn = Template(filename, template, regions)
  File "/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/template/template.py", line 44, in __init__
    self.conditions = cfnlint.conditions.Conditions(self)
  File "/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/conditions/conditions.py", line 34, in __init__
    self._cnf, self._solver_params = self._build_cnf(list(self._conditions.keys()))
  File "/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/conditions/conditions.py", line 118, in _build_cnf
    allowed_values[param.hash].remove(get_hash(equal_1.left))
ValueError: list.remove(x): x not in list
```

### Expected behavior

Expected it not to fail, or to fail like a template violation would.

### Reproduction template

Not sure yet which one is causing the issue, but this reproduces it:

```bash
git clone https://github.com/aws/serverless-application-model.git --depth 1
cd serverless-application-model
python3 -m venv .venv
.venv/bin/python -m pip install cfn-lint==0.76.0
.venv/bin/cfn-lint
```

However, if you try with `.venv/bin/python -m pip install cfn-lint==0.75.0`, it succeeds.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/cfnlint/conditions/conditions.py`
Content:
```
1 """
2 Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 SPDX-License-Identifier: MIT-0
4 """
5 import itertools
6 import logging
7 import traceback
8 from typing import Any, Dict, Iterator, List, Tuple
9 
10 from sympy import And, Implies, Not, Symbol
11 from sympy.assumptions.cnf import EncodedCNF
12 from sympy.logic.boolalg import BooleanFalse, BooleanTrue
13 from sympy.logic.inference import satisfiable
14 
15 from cfnlint.conditions._utils import get_hash
16 from cfnlint.conditions.condition import ConditionNamed
17 from cfnlint.conditions.equals import Equal
18 
19 LOGGER = logging.getLogger(__name__)
20 
21 
22 class Conditions:
23     """Conditions provides the logic for relating individual condition together"""
24 
25     _conditions: Dict[str, ConditionNamed]
26     _parameters: Dict[str, List[str]]  # Dict of parameters with AllowedValues hashed
27     _max_scenarios: int = 128  # equivalent to 2^7
28 
29     def __init__(self, cfn):
30         self._conditions = {}
31         self._parameters = {}
32         self._init_conditions(cfn=cfn)
33         self._init_parameters(cfn=cfn)
34         self._cnf, self._solver_params = self._build_cnf(list(self._conditions.keys()))
35 
36     def _init_conditions(self, cfn):
37         conditions = cfn.template.get("Conditions")
38         if isinstance(conditions, dict):
39             for k, _ in conditions.items():
40                 try:
41                     self._conditions[k] = ConditionNamed(k, conditions)
42                 except ValueError as e:
43                     LOGGER.debug(
44                         "Captured error while building condition %s: %s", k, str(e)
45                     )
46                 except Exception as e:  # pylint: disable=broad-exception-caught
47                     if LOGGER.getEffectiveLevel() == logging.DEBUG:
48                         error_message = traceback.format_exc()
49                     else:
50                         error_message = str(e)
51                     LOGGER.debug(
52                         "Captured unknown error while building condition %s: %s",
53                         k,
54                         error_message,
55                     )
56 
57     def _init_parameters(self, cfn: Any) -> None:
58         parameters = cfn.template.get("Parameters")
59         if not isinstance(parameters, dict):
60             return
61         for parameter_name, parameter in parameters.items():
62             if not isinstance(parameter, dict):
63                 continue
64             allowed_values = parameter.get("AllowedValues")
65             if not allowed_values or not isinstance(allowed_values, list):
66                 continue
67 
68             param_hash = get_hash({"Ref": parameter_name})
69             self._parameters[param_hash] = []
70             for allowed_value in allowed_values:
71                 if isinstance(allowed_value, (str, int, float, bool)):
72                     self._parameters[param_hash].append(get_hash(str(allowed_value)))
73 
74     def _build_cnf(
75         self, condition_names: List[str]
76     ) -> Tuple[EncodedCNF, Dict[str, Any]]:
77         cnf = EncodedCNF()
78 
79         # build parameters and equals into solver
80         equal_vars: Dict[str, Symbol] = {}
81 
82         equals: Dict[str, Equal] = {}
83         for condition_name in condition_names:
84             c_equals = self._conditions[condition_name].equals
85             for c_equal in c_equals:
86                 # check to see if equals already matches another one
87                 if c_equal.hash in equal_vars:
88                     continue
89 
90                 if c_equal.is_static is not None:
91                     if c_equal.is_static:
92                         equal_vars[c_equal.hash] = BooleanTrue()
93                     else:
94                         equal_vars[c_equal.hash] = BooleanFalse()
95                 else:
96                     equal_vars[c_equal.hash] = Symbol(c_equal.hash)
97                     # See if parameter in this equals is the same as another equals
98                     for param in c_equal.parameters:
99                         for e_hash, e_equals in equals.items():
100                             if param in e_equals.parameters:
101                                 # equivalent to NAND logic. We want to make sure that both equals
102                                 # are not both True at the same time
103                                 cnf.add_prop(
104                                     ~(equal_vars[c_equal.hash] & equal_vars[e_hash])
105                                 )
106                 equals[c_equal.hash] = c_equal
107 
108         # Determine if a set of conditions can never be all false
109         allowed_values = self._parameters.copy()
110         if allowed_values:
111             # iteration 1 cleans up all the hash values from allowed_values to know if we
112             # used them all
113             for _, equal_1 in equals.items():
114                 for param in equal_1.parameters:
115                     if param.hash not in allowed_values:
116                         continue
117                     if isinstance(equal_1.left, str):
118                         allowed_values[param.hash].remove(get_hash(equal_1.left))
119                     if isinstance(equal_1.right, str):
120                         allowed_values[param.hash].remove(get_hash(equal_1.right))
121 
122             # iteration 2 builds the cnf formulas to make sure any empty lists
123             # are now full not equals
124             for allowed_hash, allowed_value in allowed_values.items():
125                 # means the list is empty and all allowed values are validated
126                 # so not all equals can be false
127                 if not allowed_value:
128                     prop = None
129                     for _, equal_1 in equals.items():
130                         for param in equal_1.parameters:
131                             if allowed_hash == param.hash:
132                                 if prop is None:
133                                     prop = Not(equal_vars[equal_1.hash])
134                                 else:
135                                     prop = prop & Not(equal_vars[equal_1.hash])
136                     # Need to make sure they aren't all False
137                     # So Not(Not(Equal1) & Not(Equal2))
138                     # When Equal1 False and Equal2 False
139                     # Not(True & True) = False allowing this not to happen
140                     if prop is not None:
141                         cnf.add_prop(Not(prop))
142 
143         return (cnf, equal_vars)
144 
145     def build_scenarios(self, condition_names: List[str]) -> Iterator[Dict[str, bool]]:
146         """Given a list of condition names this function will yield scenarios that represent
147         those conditions and there result (True/False)
148 
149         Args:
150             condition_names (List[str]): A list of condition names
151 
152         Returns:
153             Iterator[Dict[str, bool]]: yield dict objects of {ConditionName: True/False}
154         """
155         # nothing to yield if there are no conditions
156         if len(condition_names) == 0:
157             return
158 
159         # if only one condition we will assume its True/False
160         if len(condition_names) == 1:
161             yield {condition_names[0]: True}
162             yield {condition_names[0]: False}
163             return
164 
165         try:
166             # build a large matric of True/False options based on the provided conditions
167             scenarios_returned = 0
168             for p in itertools.product([True, False], repeat=len(condition_names)):
169                 cnf = self._cnf.copy()
170                 params = dict(zip(condition_names, p))
171                 for condition_name, opt in params.items():
172                     if opt:
173                         cnf.add_prop(
174                             self._conditions[condition_name].build_true_cnf(
175                                 self._solver_params
176                             )
177                         )
178                     else:
179                         cnf.add_prop(
180                             self._conditions[condition_name].build_false_cnf(
181                                 self._solver_params
182                             )
183                         )
184 
185                 # if the scenario can be satisfied then return it
186                 if satisfiable(cnf):
187                     yield params
188                     scenarios_returned += 1
189 
190                 # On occassions people will use a lot of non-related conditions
191                 # this is fail safe to limit the maximum number of responses
192                 if scenarios_returned >= self._max_scenarios:
193                     return
194         except KeyError:
195             # KeyError is because the listed condition doesn't exist because of bad
196             #  formatting or just the wrong condition name
197             return
198 
199     def check_implies(self, scenarios: Dict[str, bool], implies: str) -> bool:
200         """Based on a bunch of scenario conditions and their Truth/False value
201         determine if implies condition is True any time the scenarios are satisfied
202         solver, solver_params = self._build_solver(list(scenarios.keys()) + [implies])
203 
204         Args:
205             scenarios (Dict[str, bool]): A list of condition names and if they are True or False
206             implies: the condition name that we are implying will also be True
207 
208         Returns:
209             bool: if the implied condition will be True if the scenario is True
210         """
211         try:
212             cnf = self._cnf.copy()
213             # if the implies condition has to be false in the scenarios we
214             # know it can never be true
215             if not scenarios.get(implies, True):
216                 return False
217 
218             conditions = []
219             for condition_name, opt in scenarios.items():
220                 if opt:
221                     conditions.append(
222                         self._conditions[condition_name].build_true_cnf(
223                             self._solver_params
224                         )
225                     )
226                 else:
227                     conditions.append(
228                         self._conditions[condition_name].build_false_cnf(
229                             self._solver_params
230                         )
231                     )
232 
233             implies_condition = self._conditions[implies].build_true_cnf(
234                 self._solver_params
235             )
236 
237             and_condition = And(*conditions)
238             cnf.add_prop(and_condition)
239 
240             # if the implies condition has to be true already then we don't
241             # need to imply it
242             if not scenarios.get(implies):
243                 cnf.add_prop(Not(Implies(and_condition, implies_condition)))
244             if satisfiable(cnf):
245                 return True
246 
247             return False
248         except KeyError:
249             # KeyError is because the listed condition doesn't exist because of bad
250             #  formatting or just the wrong condition name
251             return True
252
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/cfnlint/conditions/conditions.py b/src/cfnlint/conditions/conditions.py
--- a/src/cfnlint/conditions/conditions.py
+++ b/src/cfnlint/conditions/conditions.py
@@ -115,9 +115,15 @@
                     if param.hash not in allowed_values:
                         continue
                     if isinstance(equal_1.left, str):
-                        allowed_values[param.hash].remove(get_hash(equal_1.left))
+                        if get_hash(equal_1.left) in allowed_values[param.hash]:
+                            allowed_values[param.hash].remove(get_hash(equal_1.left))
+                        else:
+                            equal_vars[equal_1.hash] = BooleanFalse()
                     if isinstance(equal_1.right, str):
-                        allowed_values[param.hash].remove(get_hash(equal_1.right))
+                        if get_hash(equal_1.right) in allowed_values[param.hash]:
+                            allowed_values[param.hash].remove(get_hash(equal_1.right))
+                        else:
+                            equal_vars[equal_1.hash] = BooleanFalse()
 
             # iteration 2 builds the cnf formulas to make sure any empty lists
             # are now full not equals
@@ -157,10 +163,10 @@
             return
 
         # if only one condition we will assume its True/False
-        if len(condition_names) == 1:
-            yield {condition_names[0]: True}
-            yield {condition_names[0]: False}
-            return
+        # if len(condition_names) == 1:
+        #    yield {condition_names[0]: True}
+        #    yield {condition_names[0]: False}
+        #    return
 
         try:
             # build a large matric of True/False options based on the provided conditions

{"golden_diff": "diff --git a/src/cfnlint/conditions/conditions.py b/src/cfnlint/conditions/conditions.py\n--- a/src/cfnlint/conditions/conditions.py\n+++ b/src/cfnlint/conditions/conditions.py\n@@ -115,9 +115,15 @@\n                     if param.hash not in allowed_values:\n                         continue\n                     if isinstance(equal_1.left, str):\n-                        allowed_values[param.hash].remove(get_hash(equal_1.left))\n+                        if get_hash(equal_1.left) in allowed_values[param.hash]:\n+                            allowed_values[param.hash].remove(get_hash(equal_1.left))\n+                        else:\n+                            equal_vars[equal_1.hash] = BooleanFalse()\n                     if isinstance(equal_1.right, str):\n-                        allowed_values[param.hash].remove(get_hash(equal_1.right))\n+                        if get_hash(equal_1.right) in allowed_values[param.hash]:\n+                            allowed_values[param.hash].remove(get_hash(equal_1.right))\n+                        else:\n+                            equal_vars[equal_1.hash] = BooleanFalse()\n \n             # iteration 2 builds the cnf formulas to make sure any empty lists\n             # are now full not equals\n@@ -157,10 +163,10 @@\n             return\n \n         # if only one condition we will assume its True/False\n-        if len(condition_names) == 1:\n-            yield {condition_names[0]: True}\n-            yield {condition_names[0]: False}\n-            return\n+        # if len(condition_names) == 1:\n+        #    yield {condition_names[0]: True}\n+        #    yield {condition_names[0]: False}\n+        #    return\n \n         try:\n             # build a large matric of True/False options based on the provided conditions\n", "issue": "ValueError: list.remove(x): x not in list\n### CloudFormation Lint Version\r\n\r\n0.76.0\r\n\r\n### What operating system are you using?\r\n\r\nmacOS\r\n\r\n### Describe the bug\r\n\r\nWe run latest `cfn-lint` on all templates in the [AWS SAM repository](https://github.com/aws/serverless-application-model/); looks like 0.76.0 introduced a bug.\r\n\r\nSince today, `cfn-lint` fails with:\r\n\r\n```text\r\nTraceback (most recent call last):\r\n  File \"/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/bin/cfn-lint\", line 8, in <module>\r\n    sys.exit(main())\r\n  File \"/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/__main__.py\", line 39, in main\r\n    matches = list(cfnlint.core.get_matches(filenames, args))\r\n  File \"/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/core.py\", line 173, in get_matches\r\n    matches = run_cli(\r\n  File \"/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/core.py\", line 78, in run_cli\r\n    return run_checks(filename, template, rules, regions, mandatory_rules)\r\n  File \"/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/core.py\", line 334, in run_checks\r\n    runner = cfnlint.runner.Runner(\r\n  File \"/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/runner.py\", line 32, in __init__\r\n    self.cfn = Template(filename, template, regions)\r\n  File \"/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/template/template.py\", line 44, in __init__\r\n    self.conditions = cfnlint.conditions.Conditions(self)\r\n  File \"/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/conditions/conditions.py\", line 34, in __init__\r\n    self._cnf, self._solver_params = self._build_cnf(list(self._conditions.keys()))\r\n  File \"/Users/rehnc/Desktop/tmp/serverless-application-model/.venv_cfn_lint/lib/python3.10/site-packages/cfnlint/conditions/conditions.py\", line 118, in _build_cnf\r\n    allowed_values[param.hash].remove(get_hash(equal_1.left))\r\nValueError: list.remove(x): x not in list\r\n```\r\n\r\n### Expected behavior\r\n\r\nExpected it not to fail, or to fail like a template violation would.\r\n\r\n### Reproduction template\r\n\r\nNot sure yet which one is causing the issue, but this reproduces it:\r\n\r\n```bash\r\ngit clone https://github.com/aws/serverless-application-model.git --depth 1\r\ncd serverless-application-model\r\npython3 -m venv .venv\r\n.venv/bin/python -m pip install cfn-lint==0.76.0\r\n.venv/bin/cfn-lint\r\n```\r\n\r\nHowever, if you try with `.venv/bin/python -m pip install cfn-lint==0.75.0`, it succeeds.\n", "before_files": [{"content": "\"\"\"\nCopyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport itertools\nimport logging\nimport traceback\nfrom typing import Any, Dict, Iterator, List, Tuple\n\nfrom sympy import And, Implies, Not, Symbol\nfrom sympy.assumptions.cnf import EncodedCNF\nfrom sympy.logic.boolalg import BooleanFalse, BooleanTrue\nfrom sympy.logic.inference import satisfiable\n\nfrom cfnlint.conditions._utils import get_hash\nfrom cfnlint.conditions.condition import ConditionNamed\nfrom cfnlint.conditions.equals import Equal\n\nLOGGER = logging.getLogger(__name__)\n\n\nclass Conditions:\n    \"\"\"Conditions provides the logic for relating individual condition together\"\"\"\n\n    _conditions: Dict[str, ConditionNamed]\n    _parameters: Dict[str, List[str]]  # Dict of parameters with AllowedValues hashed\n    _max_scenarios: int = 128  # equivalent to 2^7\n\n    def __init__(self, cfn):\n        self._conditions = {}\n        self._parameters = {}\n        self._init_conditions(cfn=cfn)\n        self._init_parameters(cfn=cfn)\n        self._cnf, self._solver_params = self._build_cnf(list(self._conditions.keys()))\n\n    def _init_conditions(self, cfn):\n        conditions = cfn.template.get(\"Conditions\")\n        if isinstance(conditions, dict):\n            for k, _ in conditions.items():\n                try:\n                    self._conditions[k] = ConditionNamed(k, conditions)\n                except ValueError as e:\n                    LOGGER.debug(\n                        \"Captured error while building condition %s: %s\", k, str(e)\n                    )\n                except Exception as e:  # pylint: disable=broad-exception-caught\n                    if LOGGER.getEffectiveLevel() == logging.DEBUG:\n                        error_message = traceback.format_exc()\n                    else:\n                        error_message = str(e)\n                    LOGGER.debug(\n                        \"Captured unknown error while building condition %s: %s\",\n                        k,\n                        error_message,\n                    )\n\n    def _init_parameters(self, cfn: Any) -> None:\n        parameters = cfn.template.get(\"Parameters\")\n        if not isinstance(parameters, dict):\n            return\n        for parameter_name, parameter in parameters.items():\n            if not isinstance(parameter, dict):\n                continue\n            allowed_values = parameter.get(\"AllowedValues\")\n            if not allowed_values or not isinstance(allowed_values, list):\n                continue\n\n            param_hash = get_hash({\"Ref\": parameter_name})\n            self._parameters[param_hash] = []\n            for allowed_value in allowed_values:\n                if isinstance(allowed_value, (str, int, float, bool)):\n                    self._parameters[param_hash].append(get_hash(str(allowed_value)))\n\n    def _build_cnf(\n        self, condition_names: List[str]\n    ) -> Tuple[EncodedCNF, Dict[str, Any]]:\n        cnf = EncodedCNF()\n\n        # build parameters and equals into solver\n        equal_vars: Dict[str, Symbol] = {}\n\n        equals: Dict[str, Equal] = {}\n        for condition_name in condition_names:\n            c_equals = self._conditions[condition_name].equals\n            for c_equal in c_equals:\n                # check to see if equals already matches another one\n                if c_equal.hash in equal_vars:\n                    continue\n\n                if c_equal.is_static is not None:\n                    if c_equal.is_static:\n                        equal_vars[c_equal.hash] = BooleanTrue()\n                    else:\n                        equal_vars[c_equal.hash] = BooleanFalse()\n                else:\n                    equal_vars[c_equal.hash] = Symbol(c_equal.hash)\n                    # See if parameter in this equals is the same as another equals\n                    for param in c_equal.parameters:\n                        for e_hash, e_equals in equals.items():\n                            if param in e_equals.parameters:\n                                # equivalent to NAND logic. We want to make sure that both equals\n                                # are not both True at the same time\n                                cnf.add_prop(\n                                    ~(equal_vars[c_equal.hash] & equal_vars[e_hash])\n                                )\n                equals[c_equal.hash] = c_equal\n\n        # Determine if a set of conditions can never be all false\n        allowed_values = self._parameters.copy()\n        if allowed_values:\n            # iteration 1 cleans up all the hash values from allowed_values to know if we\n            # used them all\n            for _, equal_1 in equals.items():\n                for param in equal_1.parameters:\n                    if param.hash not in allowed_values:\n                        continue\n                    if isinstance(equal_1.left, str):\n                        allowed_values[param.hash].remove(get_hash(equal_1.left))\n                    if isinstance(equal_1.right, str):\n                        allowed_values[param.hash].remove(get_hash(equal_1.right))\n\n            # iteration 2 builds the cnf formulas to make sure any empty lists\n            # are now full not equals\n            for allowed_hash, allowed_value in allowed_values.items():\n                # means the list is empty and all allowed values are validated\n                # so not all equals can be false\n                if not allowed_value:\n                    prop = None\n                    for _, equal_1 in equals.items():\n                        for param in equal_1.parameters:\n                            if allowed_hash == param.hash:\n                                if prop is None:\n                                    prop = Not(equal_vars[equal_1.hash])\n                                else:\n                                    prop = prop & Not(equal_vars[equal_1.hash])\n                    # Need to make sure they aren't all False\n                    # So Not(Not(Equal1) & Not(Equal2))\n                    # When Equal1 False and Equal2 False\n                    # Not(True & True) = False allowing this not to happen\n                    if prop is not None:\n                        cnf.add_prop(Not(prop))\n\n        return (cnf, equal_vars)\n\n    def build_scenarios(self, condition_names: List[str]) -> Iterator[Dict[str, bool]]:\n        \"\"\"Given a list of condition names this function will yield scenarios that represent\n        those conditions and there result (True/False)\n\n        Args:\n            condition_names (List[str]): A list of condition names\n\n        Returns:\n            Iterator[Dict[str, bool]]: yield dict objects of {ConditionName: True/False}\n        \"\"\"\n        # nothing to yield if there are no conditions\n        if len(condition_names) == 0:\n            return\n\n        # if only one condition we will assume its True/False\n        if len(condition_names) == 1:\n            yield {condition_names[0]: True}\n            yield {condition_names[0]: False}\n            return\n\n        try:\n            # build a large matric of True/False options based on the provided conditions\n            scenarios_returned = 0\n            for p in itertools.product([True, False], repeat=len(condition_names)):\n                cnf = self._cnf.copy()\n                params = dict(zip(condition_names, p))\n                for condition_name, opt in params.items():\n                    if opt:\n                        cnf.add_prop(\n                            self._conditions[condition_name].build_true_cnf(\n                                self._solver_params\n                            )\n                        )\n                    else:\n                        cnf.add_prop(\n                            self._conditions[condition_name].build_false_cnf(\n                                self._solver_params\n                            )\n                        )\n\n                # if the scenario can be satisfied then return it\n                if satisfiable(cnf):\n                    yield params\n                    scenarios_returned += 1\n\n                # On occassions people will use a lot of non-related conditions\n                # this is fail safe to limit the maximum number of responses\n                if scenarios_returned >= self._max_scenarios:\n                    return\n        except KeyError:\n            # KeyError is because the listed condition doesn't exist because of bad\n            #  formatting or just the wrong condition name\n            return\n\n    def check_implies(self, scenarios: Dict[str, bool], implies: str) -> bool:\n        \"\"\"Based on a bunch of scenario conditions and their Truth/False value\n        determine if implies condition is True any time the scenarios are satisfied\n        solver, solver_params = self._build_solver(list(scenarios.keys()) + [implies])\n\n        Args:\n            scenarios (Dict[str, bool]): A list of condition names and if they are True or False\n            implies: the condition name that we are implying will also be True\n\n        Returns:\n            bool: if the implied condition will be True if the scenario is True\n        \"\"\"\n        try:\n            cnf = self._cnf.copy()\n            # if the implies condition has to be false in the scenarios we\n            # know it can never be true\n            if not scenarios.get(implies, True):\n                return False\n\n            conditions = []\n            for condition_name, opt in scenarios.items():\n                if opt:\n                    conditions.append(\n                        self._conditions[condition_name].build_true_cnf(\n                            self._solver_params\n                        )\n                    )\n                else:\n                    conditions.append(\n                        self._conditions[condition_name].build_false_cnf(\n                            self._solver_params\n                        )\n                    )\n\n            implies_condition = self._conditions[implies].build_true_cnf(\n                self._solver_params\n            )\n\n            and_condition = And(*conditions)\n            cnf.add_prop(and_condition)\n\n            # if the implies condition has to be true already then we don't\n            # need to imply it\n            if not scenarios.get(implies):\n                cnf.add_prop(Not(Implies(and_condition, implies_condition)))\n            if satisfiable(cnf):\n                return True\n\n            return False\n        except KeyError:\n            # KeyError is because the listed condition doesn't exist because of bad\n            #  formatting or just the wrong condition name\n            return True\n", "path": "src/cfnlint/conditions/conditions.py"}], "after_files": [{"content": "\"\"\"\nCopyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport itertools\nimport logging\nimport traceback\nfrom typing import Any, Dict, Iterator, List, Tuple\n\nfrom sympy import And, Implies, Not, Symbol\nfrom sympy.assumptions.cnf import EncodedCNF\nfrom sympy.logic.boolalg import BooleanFalse, BooleanTrue\nfrom sympy.logic.inference import satisfiable\n\nfrom cfnlint.conditions._utils import get_hash\nfrom cfnlint.conditions.condition import ConditionNamed\nfrom cfnlint.conditions.equals import Equal\n\nLOGGER = logging.getLogger(__name__)\n\n\nclass Conditions:\n    \"\"\"Conditions provides the logic for relating individual condition together\"\"\"\n\n    _conditions: Dict[str, ConditionNamed]\n    _parameters: Dict[str, List[str]]  # Dict of parameters with AllowedValues hashed\n    _max_scenarios: int = 128  # equivalent to 2^7\n\n    def __init__(self, cfn):\n        self._conditions = {}\n        self._parameters = {}\n        self._init_conditions(cfn=cfn)\n        self._init_parameters(cfn=cfn)\n        self._cnf, self._solver_params = self._build_cnf(list(self._conditions.keys()))\n\n    def _init_conditions(self, cfn):\n        conditions = cfn.template.get(\"Conditions\")\n        if isinstance(conditions, dict):\n            for k, _ in conditions.items():\n                try:\n                    self._conditions[k] = ConditionNamed(k, conditions)\n                except ValueError as e:\n                    LOGGER.debug(\n                        \"Captured error while building condition %s: %s\", k, str(e)\n                    )\n                except Exception as e:  # pylint: disable=broad-exception-caught\n                    if LOGGER.getEffectiveLevel() == logging.DEBUG:\n                        error_message = traceback.format_exc()\n                    else:\n                        error_message = str(e)\n                    LOGGER.debug(\n                        \"Captured unknown error while building condition %s: %s\",\n                        k,\n                        error_message,\n                    )\n\n    def _init_parameters(self, cfn: Any) -> None:\n        parameters = cfn.template.get(\"Parameters\")\n        if not isinstance(parameters, dict):\n            return\n        for parameter_name, parameter in parameters.items():\n            if not isinstance(parameter, dict):\n                continue\n            allowed_values = parameter.get(\"AllowedValues\")\n            if not allowed_values or not isinstance(allowed_values, list):\n                continue\n\n            param_hash = get_hash({\"Ref\": parameter_name})\n            self._parameters[param_hash] = []\n            for allowed_value in allowed_values:\n                if isinstance(allowed_value, (str, int, float, bool)):\n                    self._parameters[param_hash].append(get_hash(str(allowed_value)))\n\n    def _build_cnf(\n        self, condition_names: List[str]\n    ) -> Tuple[EncodedCNF, Dict[str, Any]]:\n        cnf = EncodedCNF()\n\n        # build parameters and equals into solver\n        equal_vars: Dict[str, Symbol] = {}\n\n        equals: Dict[str, Equal] = {}\n        for condition_name in condition_names:\n            c_equals = self._conditions[condition_name].equals\n            for c_equal in c_equals:\n                # check to see if equals already matches another one\n                if c_equal.hash in equal_vars:\n                    continue\n\n                if c_equal.is_static is not None:\n                    if c_equal.is_static:\n                        equal_vars[c_equal.hash] = BooleanTrue()\n                    else:\n                        equal_vars[c_equal.hash] = BooleanFalse()\n                else:\n                    equal_vars[c_equal.hash] = Symbol(c_equal.hash)\n                    # See if parameter in this equals is the same as another equals\n                    for param in c_equal.parameters:\n                        for e_hash, e_equals in equals.items():\n                            if param in e_equals.parameters:\n                                # equivalent to NAND logic. We want to make sure that both equals\n                                # are not both True at the same time\n                                cnf.add_prop(\n                                    ~(equal_vars[c_equal.hash] & equal_vars[e_hash])\n                                )\n                equals[c_equal.hash] = c_equal\n\n        # Determine if a set of conditions can never be all false\n        allowed_values = self._parameters.copy()\n        if allowed_values:\n            # iteration 1 cleans up all the hash values from allowed_values to know if we\n            # used them all\n            for _, equal_1 in equals.items():\n                for param in equal_1.parameters:\n                    if param.hash not in allowed_values:\n                        continue\n                    if isinstance(equal_1.left, str):\n                        if get_hash(equal_1.left) in allowed_values[param.hash]:\n                            allowed_values[param.hash].remove(get_hash(equal_1.left))\n                        else:\n                            equal_vars[equal_1.hash] = BooleanFalse()\n                    if isinstance(equal_1.right, str):\n                        if get_hash(equal_1.right) in allowed_values[param.hash]:\n                            allowed_values[param.hash].remove(get_hash(equal_1.right))\n                        else:\n                            equal_vars[equal_1.hash] = BooleanFalse()\n\n            # iteration 2 builds the cnf formulas to make sure any empty lists\n            # are now full not equals\n            for allowed_hash, allowed_value in allowed_values.items():\n                # means the list is empty and all allowed values are validated\n                # so not all equals can be false\n                if not allowed_value:\n                    prop = None\n                    for _, equal_1 in equals.items():\n                        for param in equal_1.parameters:\n                            if allowed_hash == param.hash:\n                                if prop is None:\n                                    prop = Not(equal_vars[equal_1.hash])\n                                else:\n                                    prop = prop & Not(equal_vars[equal_1.hash])\n                    # Need to make sure they aren't all False\n                    # So Not(Not(Equal1) & Not(Equal2))\n                    # When Equal1 False and Equal2 False\n                    # Not(True & True) = False allowing this not to happen\n                    if prop is not None:\n                        cnf.add_prop(Not(prop))\n\n        return (cnf, equal_vars)\n\n    def build_scenarios(self, condition_names: List[str]) -> Iterator[Dict[str, bool]]:\n        \"\"\"Given a list of condition names this function will yield scenarios that represent\n        those conditions and there result (True/False)\n\n        Args:\n            condition_names (List[str]): A list of condition names\n\n        Returns:\n            Iterator[Dict[str, bool]]: yield dict objects of {ConditionName: True/False}\n        \"\"\"\n        # nothing to yield if there are no conditions\n        if len(condition_names) == 0:\n            return\n\n        # if only one condition we will assume its True/False\n        # if len(condition_names) == 1:\n        #    yield {condition_names[0]: True}\n        #    yield {condition_names[0]: False}\n        #    return\n\n        try:\n            # build a large matric of True/False options based on the provided conditions\n            scenarios_returned = 0\n            for p in itertools.product([True, False], repeat=len(condition_names)):\n                cnf = self._cnf.copy()\n                params = dict(zip(condition_names, p))\n                for condition_name, opt in params.items():\n                    if opt:\n                        cnf.add_prop(\n                            self._conditions[condition_name].build_true_cnf(\n                                self._solver_params\n                            )\n                        )\n                    else:\n                        cnf.add_prop(\n                            self._conditions[condition_name].build_false_cnf(\n                                self._solver_params\n                            )\n                        )\n\n                # if the scenario can be satisfied then return it\n                if satisfiable(cnf):\n                    yield params\n                    scenarios_returned += 1\n\n                # On occassions people will use a lot of non-related conditions\n                # this is fail safe to limit the maximum number of responses\n                if scenarios_returned >= self._max_scenarios:\n                    return\n        except KeyError:\n            # KeyError is because the listed condition doesn't exist because of bad\n            #  formatting or just the wrong condition name\n            return\n\n    def check_implies(self, scenarios: Dict[str, bool], implies: str) -> bool:\n        \"\"\"Based on a bunch of scenario conditions and their Truth/False value\n        determine if implies condition is True any time the scenarios are satisfied\n        solver, solver_params = self._build_solver(list(scenarios.keys()) + [implies])\n\n        Args:\n            scenarios (Dict[str, bool]): A list of condition names and if they are True or False\n            implies: the condition name that we are implying will also be True\n\n        Returns:\n            bool: if the implied condition will be True if the scenario is True\n        \"\"\"\n        try:\n            cnf = self._cnf.copy()\n            # if the implies condition has to be false in the scenarios we\n            # know it can never be true\n            if not scenarios.get(implies, True):\n                return False\n\n            conditions = []\n            for condition_name, opt in scenarios.items():\n                if opt:\n                    conditions.append(\n                        self._conditions[condition_name].build_true_cnf(\n                            self._solver_params\n                        )\n                    )\n                else:\n                    conditions.append(\n                        self._conditions[condition_name].build_false_cnf(\n                            self._solver_params\n                        )\n                    )\n\n            implies_condition = self._conditions[implies].build_true_cnf(\n                self._solver_params\n            )\n\n            and_condition = And(*conditions)\n            cnf.add_prop(and_condition)\n\n            # if the implies condition has to be true already then we don't\n            # need to imply it\n            if not scenarios.get(implies):\n                cnf.add_prop(Not(Implies(and_condition, implies_condition)))\n            if satisfiable(cnf):\n                return True\n\n            return False\n        except KeyError:\n            # KeyError is because the listed condition doesn't exist because of bad\n            #  formatting or just the wrong condition name\n            return True\n", "path": "src/cfnlint/conditions/conditions.py"}]}