diff --git a/TP2/constant_propagation.py b/TP2/constant_propagation.py
index a530c34071106acc53ced1ee96915ab2cb9deb93..a7afd78aeb3483b17dd0411c0e065a878c0e8f14 100644
--- a/TP2/constant_propagation.py
+++ b/TP2/constant_propagation.py
@@ -7,13 +7,17 @@ from cfg import Label, LSkip, LSet, LTest, LErr
 from iteration import Transfer
 from syntax import *
 
+
 @dataclass
 class abstract_value[T]:
     """None indicates that we don't know the precise value"""
+
     value: T | None
+
     def __str__(self):
         return f"{self.value}"
 
+
 type abstract_env = dict[str, abstract_value[int]]
 """mapping from variables to abstract values.
 
@@ -24,84 +28,171 @@ type state = abstract_env | None
 """abstract state is either an abstract env or bottom
 """
 
+
 def eval_aexp(env: abstract_env, e: ArithExpr) -> abstract_value[int] | None:
     """evaluate an arithmetic expression in an abstract environment
     returns None in case of error
     """
     match e:
-        case AECst(value): return abstract_value(value)
+        case AECst(value):
+            return abstract_value(value)
         case AEVar(var):
-            if var in env: return env[var]
-            else: return None
-        case AEUop(uop,expr):
-            res = eval_aexp(env,expr)
-            if res is None: return None
-            if res.value is None: return abstract_value(None)
-            if uop == Uop.OPP: return abstract_value(-res.value)
+            if var in env:
+                return env[var]
+            else:
+                return None
+        case AEUop(uop, expr):
+            res = eval_aexp(env, expr)
+            if res is None:
+                return None
+            if res.value is None:
+                return abstract_value(None)
+            if uop == Uop.OPP:
+                return abstract_value(-res.value)
             return None
-        case AEBop(bop,left_expr,right_expr):
-            v1 = eval_aexp(env,left_expr)
-            v2 = eval_aexp(env,right_expr)
-            if v1 is None or v2 is None: return None
+        case AEBop(bop, left_expr, right_expr):
+            v1 = eval_aexp(env, left_expr)
+            v2 = eval_aexp(env, right_expr)
+            if v1 is None or v2 is None:
+                return None
             if v1.value is None or v2.value is None:
                 return abstract_value(None)
             match bop:
-                case Bop.ADD: return abstract_value(v1.value+v2.value)
-                case Bop.MUL: return abstract_value(v1.value*v2.value)
+                case Bop.ADD:
+                    return abstract_value(v1.value + v2.value)
+                case Bop.MUL:
+                    return abstract_value(v1.value * v2.value)
                 case Bop.DIV:
-                    if v2.value == 0: return None
-                    return abstract_value(v1.value//v2.value)
-        case _: assert False
+                    if v2.value == 0:
+                        return None
+                    return abstract_value(v1.value // v2.value)
+        case _:
+            assert False
+
 
 def eval_bexp(env: abstract_env, e: BoolExpr) -> abstract_value[bool] | None:
     """abstract evaluation of a boolean expression"""
     match e:
         case BEPlain(aexpr):
             res = eval_aexp(env, aexpr)
-            if res is None: return None
-            if res.value is None: return abstract_value(None)
-            return abstract_value(res.value!=0)
-        case BEEq(left_expr,right_expr):
+            if res is None:
+                return None
+            if res.value is None:
+                return abstract_value(None)
+            return abstract_value(res.value != 0)
+        case BEEq(left_expr, right_expr):
             v1 = eval_aexp(env, left_expr)
             v2 = eval_aexp(env, right_expr)
-            if v1 is None or v2 is None: return None
-            if v1.value is None or v2.value is None: return abstract_value(None)
+            if v1 is None or v2 is None:
+                return None
+            if v1.value is None or v2.value is None:
+                return abstract_value(None)
             return abstract_value(v1.value == v2.value)
-        case BELeq(left_expr,right_expr):
+        case BELeq(left_expr, right_expr):
             v1 = eval_aexp(env, left_expr)
             v2 = eval_aexp(env, right_expr)
-            if v1 is None or v2 is None: return None
-            if v1.value is None or v2.value is None: return abstract_value(None)
+            if v1 is None or v2 is None:
+                return None
+            if v1.value is None or v2.value is None:
+                return abstract_value(None)
             return abstract_value(v1.value <= v2.value)
         case BENeg(expr):
-            v = eval_bexp(env,expr)
-            if v is None: return None
-            if v.value is None: return v
+            v = eval_bexp(env, expr)
+            if v is None:
+                return None
+            if v.value is None:
+                return v
             return abstract_value(not v.value)
-        case _: assert False
+        case _:
+            assert False
+
 
 class Constant_propagation(Transfer[state]):
     variables: frozenset[str]
-    def __init__(self,instr):
+
+    def __init__(self, instr):
         self.variables = variables_of_instr(instr)
+
     def bottom(self) -> state:
-        ...
+        return None
+
     def init_state(self) -> state:
-        ...
-    def join(self,s1:state,s2:state) -> state:
-        ...
+        return {var: abstract_value(None) for var in self.variables}
 
-    def included(self,s1: state,s2: state) -> bool:
-        ...
+    def join(self, s1: state, s2: state) -> state:
+        if s1 is None:
+            return s2
+        if s2 is None:
+            return s1
+        new_env = {}
+        for var in self.variables:
+            v1 = s1.get(var, abstract_value(None))
+            v2 = s2.get(var, abstract_value(None))
+            if v1.value == v2.value:
+                new_env[var] = abstract_value(v1.value)
+            else:
+                new_env[var] = abstract_value(None)
+        return new_env
 
-    def tr_skip(self,s: state) -> state:
-        ...
+    def included(self, s1: state, s2: state) -> bool:
+        if s1 is None:
+            return True
+        if s2 is None:
+            return False
+        for var in self.variables:
+            v1 = s1.get(var, abstract_value(None))
+            v2 = s2.get(var, abstract_value(None))
+            if v1.value is not None and v2.value != v1.value:
+                return False
+        return True
 
-    def tr_set(self,s: state,v: str,e: ArithExpr) -> state:
-        ...
+    def tr_skip(self, s: state) -> state:
+        return s
 
-    def tr_test(self,s: state,c: BoolExpr) -> state:
-        ...
+    def tr_set(self, s: state, v: str, e: ArithExpr) -> state:
+        if s is None:
+            return None
+        val = eval_aexp(s, e)
+        if val is None:
+            return None
+        new_env = s.copy()
+        new_env[v] = val
+        return new_env
+
+    def tr_test(self, s: state, c: BoolExpr) -> state:
+        if s is None:
+            return None
+        res = eval_bexp(s, c)
+        if res is None:
+            return s
+        if res.value is False:
+            return None
+        if isinstance(c, BEEq):
+            left, right = c.left_expr, c.right_expr
+            if isinstance(left, AEVar) and isinstance(right, AECst):
+                new_env = s.copy()
+                new_env[left.var] = abstract_value(right.value)
+                return new_env
+            if isinstance(right, AEVar) and isinstance(left, AECst):
+                new_env = s.copy()
+                new_env[right.var] = abstract_value(left.value)
+                return new_env
+        return s
 
-    def tr_err(self,s: state,e: Expr) -> state:
-        ...
+    def tr_err(self, s: state, e: Expr) -> state:
+        if s is None:
+            return None
+        if isinstance(e, ArithExpr):
+            res = eval_aexp(s, e)
+            if res is not None and res.value is not None:
+                return None
+            else:
+                return s
+        elif isinstance(e, BoolExpr):
+            res = eval_bexp(s, e)
+            if res is not None and res.value is not None:
+                return None
+            else:
+                return s
+        else:
+            assert False
diff --git a/TP2/example_opsem.py b/TP2/example_opsem.py
index bcc5dca4e8bbb042e3de024382c4cab6ac304b65..710e738ef27e05240013b51fc3ba2ef620ba8c72 100644
--- a/TP2/example_opsem.py
+++ b/TP2/example_opsem.py
@@ -6,16 +6,18 @@ y = AEVar("y")
 zero = AECst(0)
 one = AECst(1)
 xneg = BELeq(x, zero)
-bound_check = ICond(xneg,ISet("x",one),ISkip())
-init_y = ISet("y",one)
-body = ISeq(ISet("y",AEBop(Bop.MUL,y,x)),ISet("x",AEBop(Bop.ADD,x,AEUop(Uop.OPP,one))))
-loop = ILoop(BENeg(BELeq(x,zero)),body)
-factorial = ISeq(ISeq(bound_check,init_y),loop)
+bound_check = ICond(xneg, ISet("x", one), ISkip())
+init_y = ISet("y", one)
+body = ISeq(
+    ISet("y", AEBop(Bop.MUL, y, x)), ISet("x", AEBop(Bop.ADD, x, AEUop(Uop.OPP, one)))
+)
+loop = ILoop(BENeg(BELeq(x, zero)), body)
+factorial = ISeq(ISeq(bound_check, init_y), loop)
 
 print(f"prog is\n{factorial}")
 
-env = { "x" : 4 }
-envf = eval_instr(env,factorial)
+env = {"x": 4}
+envf = eval_instr(env, factorial)
 
 print(f"init is \n{env}")
 print(f"result is\n{envf}")
diff --git a/TP2/iteration.py b/TP2/iteration.py
index f91169661c566371dc324842b44f562fe95b4d76..9af0e5a2f2f6b45350240b081f92b96d70be44d1 100644
--- a/TP2/iteration.py
+++ b/TP2/iteration.py
@@ -5,42 +5,87 @@ each static analysis.
 - fixpoint_iteration computes a fixpoint over a cfg for the given
 transfer instance
 """
+
 from cfg import *
 from typing import Protocol
 
+
 class Transfer[S](Protocol):
     """lists the functions that must be implemented for a static analysis over Lattice S"""
+
     def bottom(self) -> S:
         """the empty state, associated to unvisited nodes"""
         ...
+
     def init_state(self) -> S:
         """the state associated to the initial node"""
         ...
-    def join(self,s1:S,s2:S) -> S:
+
+    def join(self, s1: S, s2: S) -> S:
         """join of two states when merging branches"""
         ...
-    def included(self,s1: S,s2: S) -> bool:
+
+    def included(self, s1: S, s2: S) -> bool:
         """return True when the first state is included in the second"""
         ...
-    def tr_skip(self,s: S) -> S:
+
+    def tr_skip(self, s: S) -> S:
         """transfer state across a Skip transition
 
         Almost always transfer the state unchanged
         """
         return s
-    def tr_set(self,s: S,v: str,e: ArithExpr) -> S:
+
+    def tr_set(self, s: S, v: str, e: ArithExpr) -> S:
         """transfer state across an assignment"""
         ...
-    def tr_test(self,s: S,c: BoolExpr) -> S:
+
+    def tr_test(self, s: S, c: BoolExpr) -> S:
         """transfer state across a test transition"""
         ...
-    def tr_err(self,s: S,e: Expr) -> S:
+
+    def tr_err(self, s: S, e: Expr) -> S:
         """transfer state across a transition to error state"""
         ...
 
-def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]:
+
+def fixpoint_iteration[T](transfer: Transfer, cfg: Cfg) -> dict[Node, T]:
     """computes the fixpoint for the given transfer functions over the given CfG
- 
-   output maps each node to the computed state
+
+    output maps each node to the computed state
     """
-    raise NotImplementedError
+    mapping: dict[Node, T] = {}
+    for node in cfg.g.nodes:
+        if node == cfg.init_node:
+            mapping[node] = transfer.init_state()
+        else:
+            mapping[node] = transfer.bottom()
+
+    worklist = [(cfg.init_node, dest, lab) for (dest, lab) in cfg.init_node.succs]
+
+    while worklist:
+        src, dst, label = worklist.pop(0)
+        s_src = mapping[src]
+        if isinstance(label, LSkip):
+            s_new = transfer.tr_skip(s_src)
+        elif isinstance(label, LSet):
+            s_new = transfer.tr_set(s_src, label.var, label.expr)
+        elif isinstance(label, LTest):
+            s_new = transfer.tr_test(s_src, label.cond)
+        elif isinstance(label, LErr):
+            s_new = transfer.tr_err(s_src, label.err)
+        else:
+            s_new = s_src
+        new_state = transfer.join(mapping[dst], s_new)
+        if not transfer.included(mapping[dst], new_state):
+            mapping[dst] = new_state
+            for succ, lab in dst.succs:
+                worklist.append((dst, succ, lab))
+    return mapping
+
+
+"""
+# À quoi correspondent les nœuds indiqués comme inatteignables par l'analyse?
+
+Les nœuds inatteignables correspondent aux branches du programme qui ne peuvent jamais être exécutées, car leur condition s’évalue toujours à False dans l’analyse.
+"""
diff --git a/TP2/opsem.py b/TP2/opsem.py
index 1313e985e2c11ca43cc8fcaa926eec02eba135f1..1c3507dc5740ff84b845139d63379604f311d2c1 100644
--- a/TP2/opsem.py
+++ b/TP2/opsem.py
@@ -1,50 +1,105 @@
-"""
-Operational semantics for the Why language
-
-Provides evaluation functions for the three syntactic classes,
-based on a environment mapping variables to integer values.
-These functions return None when an error occur somewhere (Big step semantics)
-"""
-
 from syntax import *
 
 def eval_aexp(env: dict[str,int], exp: ArithExpr) -> int | None:
     """evaluates an arithmetic expression"""
     match exp:
         case AECst(value):
-            raise NotImplementedError
+            return value
         case AEVar(var):
-            raise NotImplementedError
-        case AEUop(uop,expr):
-            raise NotImplementedError
-        case AEBop(bop,left_expr,right_expr):
-            raise NotImplementedError
-        case _: assert False
+            return env.get(var, None)
+        case AEUop(uop, expr):
+            v = eval_aexp(env, expr)
+            if v is None:
+                return None
+            match uop:
+                case Uop.OPP:
+                    return -v
+                case _:
+                    return None
+        case AEBop(bop, left_expr, right_expr):
+            left_val = eval_aexp(env, left_expr)
+            right_val = eval_aexp(env, right_expr)
+            if left_val is None or right_val is None:
+                return None
+            match bop:
+                case Bop.ADD:
+                    return left_val + right_val
+                case Bop.MUL:
+                    return left_val * right_val
+                case Bop.DIV:
+                    if right_val == 0:
+                        return None
+                    return left_val // right_val
+                case _:
+                    return None
+        case _:
+            assert False
 
-def eval_bexp(env: dict[str,int], exp:BoolExpr) -> bool | None:
+def eval_bexp(env: dict[str,int], exp: BoolExpr) -> bool | None:
     """evaluates a boolean expression"""
     match exp:
         case BEPlain(aexpr):
-            raise NotImplementedError
-        case BEEq(left_expr,right_expr):
-            raise NotImplementedError
-        case BELeq(left_expr,right_expr):
-            raise NotImplementedError
+            val = eval_aexp(env, aexpr)
+            if val is None:
+                return None
+            return val != 0
+        case BEEq(left_expr, right_expr):
+            left_val = eval_aexp(env, left_expr)
+            right_val = eval_aexp(env, right_expr)
+            if left_val is None or right_val is None:
+                return None
+            return left_val == right_val
+        case BELeq(left_expr, right_expr):
+            left_val = eval_aexp(env, left_expr)
+            right_val = eval_aexp(env, right_expr)
+            if left_val is None or right_val is None:
+                return None
+            return left_val <= right_val
         case BENeg(expr):
-            raise NotImplementedError
-        case _: assert False
+            val = eval_bexp(env, expr)
+            if val is None:
+                return None
+            return not val
+        case _:
+            assert False
 
 def eval_instr(env: dict[str,int], instr: Instr) -> dict[str,int] | None:
     """evaluates an instruction"""
     match instr:
         case ISkip():
-            raise NotImplementedError
-        case ISet(var,expr):
-            raise NotImplementedError
-        case ISeq(first,second):
-            raise NotImplementedError
-        case ICond(cond,true_branch,false_branch):
-            raise NotImplementedError
-        case ILoop(cond,body):
-            raise NotImplementedError
-        case _: assert False
+            return env
+        case ISet(var, expr):
+            val = eval_aexp(env, expr)
+            if val is None:
+                return None
+            new_env = env.copy()
+            new_env[var] = val
+            return new_env
+        case ISeq(first, second):
+            env1 = eval_instr(env, first)
+            if env1 is None:
+                return None
+            return eval_instr(env1, second)
+        case ICond(cond, true_branch, false_branch):
+            b = eval_bexp(env, cond)
+            if b is None:
+                return None
+            if b:
+                return eval_instr(env, true_branch)
+            else:
+                return eval_instr(env, false_branch)
+        case ILoop(cond, body):
+            current_env = env
+            while True:
+                b = eval_bexp(current_env, cond)
+                if b is None:
+                    return None
+                if not b:
+                    break
+                new_env = eval_instr(current_env, body)
+                if new_env is None:
+                    return None
+                current_env = new_env
+            return current_env
+        case _:
+            assert False
\ No newline at end of file