From a9ba5d9bebc446d18f2acb14f833c3275bf50655 Mon Sep 17 00:00:00 2001
From: Yassine Ettayeb <yassine.ettayeb@student-cs.fr>
Date: Fri, 28 Mar 2025 09:20:20 +0100
Subject: [PATCH] add TP3 files empty

---
 Pystan/config/__pycache__/cfg.cpython-312.pyc | Bin 0 -> 8171 bytes
 .../constant_propagation.cpython-312.pyc      | Bin 0 -> 7298 bytes
 .../__pycache__/iteration.cpython-312.pyc     | Bin 0 -> 4729 bytes
 .../config/__pycache__/opsem.cpython-312.pyc  | Bin 0 -> 3888 bytes
 .../config/__pycache__/syntax.cpython-312.pyc | Bin 0 -> 10228 bytes
 Pystan/config/example_interval.py             |   4 +
 Pystan/config/example_interval_eval_exp.py    |  32 ++
 Pystan/config/example_interval_reduce.py      |  39 ++
 Pystan/config/example_sign.py                 |   4 +
 Pystan/config/example_sign_eval_exp.py        |  28 ++
 Pystan/config/example_sign_reduce.py          |  33 ++
 Pystan/config/example_widen.py                |  61 +++
 Pystan/config/interval_analysis.py            | 406 ++++++++++++++++++
 Pystan/config/sign_analysis.py                | 338 +++++++++++++++
 14 files changed, 945 insertions(+)
 create mode 100644 Pystan/config/__pycache__/cfg.cpython-312.pyc
 create mode 100644 Pystan/config/__pycache__/constant_propagation.cpython-312.pyc
 create mode 100644 Pystan/config/__pycache__/iteration.cpython-312.pyc
 create mode 100644 Pystan/config/__pycache__/opsem.cpython-312.pyc
 create mode 100644 Pystan/config/__pycache__/syntax.cpython-312.pyc
 create mode 100644 Pystan/config/example_interval.py
 create mode 100644 Pystan/config/example_interval_eval_exp.py
 create mode 100644 Pystan/config/example_interval_reduce.py
 create mode 100644 Pystan/config/example_sign.py
 create mode 100644 Pystan/config/example_sign_eval_exp.py
 create mode 100644 Pystan/config/example_sign_reduce.py
 create mode 100644 Pystan/config/example_widen.py
 create mode 100644 Pystan/config/interval_analysis.py
 create mode 100644 Pystan/config/sign_analysis.py

diff --git a/Pystan/config/__pycache__/cfg.cpython-312.pyc b/Pystan/config/__pycache__/cfg.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d1a918da4587190ec0ed293eb58376cf5c5ba3d9
GIT binary patch
literal 8171
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

literal 0
HcmV?d00001

diff --git a/Pystan/config/__pycache__/constant_propagation.cpython-312.pyc b/Pystan/config/__pycache__/constant_propagation.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..81e97440258bf50a9ef41dacea62fdda37d6c587
GIT binary patch
literal 7298
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
IEOX_*0K;G*DgXcg

literal 0
HcmV?d00001

diff --git a/Pystan/config/__pycache__/iteration.cpython-312.pyc b/Pystan/config/__pycache__/iteration.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..68239e3f063ee6b0b9390a069426261f16cd1692
GIT binary patch
literal 4729
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

literal 0
HcmV?d00001

diff --git a/Pystan/config/__pycache__/opsem.cpython-312.pyc b/Pystan/config/__pycache__/opsem.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b416b2b688a0eff7fd24790cc3a7dd80c0147da9
GIT binary patch
literal 3888
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

literal 0
HcmV?d00001

diff --git a/Pystan/config/__pycache__/syntax.cpython-312.pyc b/Pystan/config/__pycache__/syntax.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0e0cff17c20d02adfbd1992c523683af47651381
GIT binary patch
literal 10228
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

literal 0
HcmV?d00001

diff --git a/Pystan/config/example_interval.py b/Pystan/config/example_interval.py
new file mode 100644
index 0000000..8aa35e0
--- /dev/null
+++ b/Pystan/config/example_interval.py
@@ -0,0 +1,4 @@
+from interval_analysis import analyze
+from example_opsem import factorial
+
+analyze(factorial)
diff --git a/Pystan/config/example_interval_eval_exp.py b/Pystan/config/example_interval_eval_exp.py
new file mode 100644
index 0000000..1203cab
--- /dev/null
+++ b/Pystan/config/example_interval_eval_exp.py
@@ -0,0 +1,32 @@
+from syntax import *
+from interval_analysis import eval_aexp, eval_bexp, abstract_env, Interval
+
+x: ArithExpr = AEVar("x")
+y: ArithExpr = AEVar("y")
+
+# x - y * (x / y)
+test_aexp: ArithExpr = AEBop(Bop.ADD,x, AEUop(Uop.OPP,AEBop(Bop.MUL,y,AEBop(Bop.DIV,x,y))))
+
+# ! (x - y <= y * (x - x/y))
+test_bexp: BoolExpr = BENeg(BELeq(AEBop(Bop.ADD,x,AEUop(Uop.OPP,y)),test_aexp))
+
+def test(env: abstract_env) -> None:
+    res_a = eval_aexp(env,test_aexp)
+    res_b = eval_bexp(env,test_bexp)
+    print(f'In environment { {var: str(i) for (var,i) in env.items()} }')
+    print(f"{test_aexp} -> {res_a}")
+    print(f"{test_bexp} -> {res_b}")
+
+test({ 'x': Interval(20,20), 'y': Interval(2,2) })
+
+test({ 'x': Interval(10,20), 'y': Interval(1,2) })
+
+test({ 'x': Interval(-30,0), 'y': Interval(-10,0) })
+
+test({ 'x': Interval(-20,20), 'y': Interval(0,0) })
+
+test({ 'x': Interval(0,20), 'y': Interval(2,5) })
+
+test({ 'x': Interval(-10,20), 'y': Interval(-5,-2) })
+
+test({})
diff --git a/Pystan/config/example_interval_reduce.py b/Pystan/config/example_interval_reduce.py
new file mode 100644
index 0000000..2d7cdda
--- /dev/null
+++ b/Pystan/config/example_interval_reduce.py
@@ -0,0 +1,39 @@
+from syntax import *
+from interval_analysis import state, abstract_env, Interval, reduce_state
+
+x: ArithExpr = AEVar("x")
+y: ArithExpr = AEVar("y")
+
+test_eq: BoolExpr = BEEq(x,y)
+
+test_neq: BoolExpr = BENeg(test_eq)
+
+test_leq: BoolExpr = BELeq(x,y)
+
+test_gt: BoolExpr = BENeg(test_leq)
+
+def env_str(env: abstract_env) -> str:
+    return '{ ' + ', '.join([ f'{var}: {i}' for (var,i) in env.items()]) + ' }'
+
+def state_str(env: state) -> str:
+    if env is None: return 'None'
+    return env_str(env)
+
+def test(env: abstract_env) -> None:
+    res_eq = reduce_state(env,test_eq)
+    res_neq = reduce_state(env,test_neq)
+    res_leq = reduce_state(env,test_leq)
+    res_gt = reduce_state(env,test_gt)
+    print(f'In environment { env_str(env) }')
+    print(f"{test_eq} -> {state_str(res_eq)}")
+    print(f"{test_neq} -> {state_str(res_neq)}")
+    print(f"{test_leq} -> {state_str(res_leq)}")
+    print(f"{test_gt} -> {state_str(res_gt)}")
+
+test({ 'x': Interval(0,5), 'y': Interval(-5,0) })
+
+test({ 'x': Interval(-5,5), 'y': Interval(3,7) })
+
+test({ 'x': Interval(0,5), 'y': Interval(-10,-5) })
+
+test({ 'x': Interval(0,0), 'y': Interval(0,4) })
diff --git a/Pystan/config/example_sign.py b/Pystan/config/example_sign.py
new file mode 100644
index 0000000..6aa6de3
--- /dev/null
+++ b/Pystan/config/example_sign.py
@@ -0,0 +1,4 @@
+from sign_analysis import analyze
+from example_opsem import factorial
+
+analyze(factorial)
diff --git a/Pystan/config/example_sign_eval_exp.py b/Pystan/config/example_sign_eval_exp.py
new file mode 100644
index 0000000..c8bdd99
--- /dev/null
+++ b/Pystan/config/example_sign_eval_exp.py
@@ -0,0 +1,28 @@
+from syntax import *
+from sign_analysis import eval_aexp, eval_bexp, abstract_env, sign
+
+x: ArithExpr = AEVar("x")
+y: ArithExpr = AEVar("y")
+
+# y * (x - x / y)
+test_aexp: ArithExpr = AEBop(Bop.MUL,y,AEBop(Bop.ADD,x, AEUop(Uop.OPP,AEBop(Bop.DIV,x,y))))
+
+# ! (x - y <= y * (x - x/y))
+test_bexp: BoolExpr = BENeg(BELeq(AEBop(Bop.ADD,x,AEUop(Uop.OPP,y)),test_aexp))
+
+def test(env: abstract_env) -> None:
+    res_a = eval_aexp(env,test_aexp)
+    res_b = eval_bexp(env,test_bexp)
+    print(f'In environment { {var: str(sign) for (var,sign) in env.items()} }')
+    print(f"{test_aexp} -> {res_a}")
+    print(f"{test_bexp} -> {res_b}")
+
+test({ 'x': sign.SPOS, 'y': sign.SPOS })
+
+test({ 'x': sign.NEG, 'y': sign.NEG })
+
+test({ 'x': sign.INT, 'y': sign.Z })
+
+test({ 'x': sign.POS, 'y': sign.NZ })
+
+test({})
diff --git a/Pystan/config/example_sign_reduce.py b/Pystan/config/example_sign_reduce.py
new file mode 100644
index 0000000..17ed636
--- /dev/null
+++ b/Pystan/config/example_sign_reduce.py
@@ -0,0 +1,33 @@
+from syntax import *
+from sign_analysis import state, abstract_env, sign, reduce_state
+
+x: ArithExpr = AEVar("x")
+y: ArithExpr = AEVar("y")
+
+test_eq: BoolExpr = BEEq(x,y)
+
+test_neq: BoolExpr = BENeg(test_eq)
+
+test_leq: BoolExpr = BELeq(x,y)
+
+test_gt: BoolExpr = BENeg(test_leq)
+
+def test(env: abstract_env) -> None:
+    res_eq = reduce_state(env,test_eq)
+    res_neq = reduce_state(env,test_neq)
+    res_leq = reduce_state(env,test_leq)
+    res_gt = reduce_state(env,test_gt)
+    print(f'In environment { {var: str(sign) for (var,sign) in env.items()} }')
+    print(f"{test_eq} -> {res_eq}")
+    print(f"{test_neq} -> {res_neq}")
+    print(f"{test_leq} -> {res_leq}")
+    print(f"{test_gt} -> {res_gt}")
+
+test({ 'x': sign.POS, 'y': sign.NEG })
+
+test({ 'x': sign.NZ, 'y': sign.NEG })
+
+test({ 'x': sign.POS, 'y': sign.SNEG })
+
+test({ 'x': sign.Z, 'y': sign.POS })
+
diff --git a/Pystan/config/example_widen.py b/Pystan/config/example_widen.py
new file mode 100644
index 0000000..c9372be
--- /dev/null
+++ b/Pystan/config/example_widen.py
@@ -0,0 +1,61 @@
+from iteration import Transfer, fixpoint_iteration
+from syntax import *
+from cfg import Cfg
+from example_opsem import factorial
+
+@dataclass
+class Number_step:
+    bound: int | None
+    def __str__(self):
+        if self.bound is None: return "+∞"
+        else: return str(self.bound)
+
+type S = Number_step | None
+
+class Analysis(Transfer[S]):
+    def bottom(self) -> S:
+        return None
+
+    def init_state(self) -> S:
+        return Number_step(0)
+
+    def join(self,s1:S,s2:S) -> S:
+        if s1 is None: return s2
+        if s2 is None: return s1
+        if s1.bound is None: return s1
+        if s2.bound is None: return s2
+        return Number_step(max(s1.bound, s2.bound))
+
+    def widen(self, s1:S, s2:S) -> S:
+        if s1 is None: return s2
+        if s2 is None: return s1
+        if s1.bound is None: return s1
+        if s2.bound is None: return s2
+        if s1.bound < s2.bound: return Number_step(None)
+        return s1
+
+    def included(self,s1: S,s2: S) -> bool:
+        if s1 is None: return True
+        if s2 is None: return False
+        if s2.bound is None: return True
+        if s1.bound is None: return False
+        return s1.bound <= s2.bound
+
+    def tr_skip(self,s: S) -> S:
+        if s is None: return None
+        if s.bound is None: return s
+        return Number_step(s.bound + 1)
+
+    def tr_set(self,s: S,v: str,e: ArithExpr) -> S:
+        return self.tr_skip(s)
+
+    def tr_test(self,s: S,c: BoolExpr) -> S:
+        return self.tr_skip(s)
+
+    def tr_err(self,s: S,e: Expr) -> S:
+        return self.tr_skip(s)
+
+res = fixpoint_iteration(Analysis(),Cfg(factorial))
+
+for (node, nb) in res.items():
+    print(f'{node} -> {nb}')
diff --git a/Pystan/config/interval_analysis.py b/Pystan/config/interval_analysis.py
new file mode 100644
index 0000000..9186efb
--- /dev/null
+++ b/Pystan/config/interval_analysis.py
@@ -0,0 +1,406 @@
+# pyright: strict
+
+"""
+implements a constant propagation analysis
+"""
+
+from dataclasses import dataclass
+from cfg import Cfg
+from iteration import Transfer, fixpoint_iteration
+from syntax import *
+
+@dataclass
+class Interval:
+    """ potentially unbounded interval
+
+    None in either bound means that we don't have info.
+    Hence `Interval(None,None)` denotes all integers
+    """
+    lower_bound: int | None
+    upper_bound: int | None
+    def __str__(self):
+        if self.lower_bound is None:
+            l = "-∞"
+        else:
+            l = str(self.lower_bound)
+        if self.upper_bound is None:
+            u = "+∞"
+        else:
+            u = str(self.upper_bound)
+        return f"[{l}; {u}]"
+
+class Top:
+    def __str__(self): return "TOP"
+
+@dataclass
+class BTop:
+    """class used for the evaluation of boolean expressions
+
+    BTop(False) indicates that we don't know if the result is True or False, but
+    that the evaluation cannot lead to an error
+    BTop(True) indicates that we neither know the result nor whether an error can occur
+    """
+    has_error: bool
+    def __str__(self):
+        if self.has_error: return "TOP (maybe error)"
+        else: return "TOP (no error)"
+
+type abstract_env = dict[str, Interval]
+"""mapping from variables to abstract values.
+
+As with concrete environment, a variable not in the dictionary will
+lead to an error if we try to obtain its value
+"""
+
+type state = abstract_env | None
+"""abstract state is either an abstract env or bottom
+"""
+
+def interval_leq(i1: Interval, i2: Interval) -> bool:
+    if i2.lower_bound is None:
+        if i2.upper_bound is None:
+            return True
+        if i1.upper_bound is None:
+            return False
+        return i1.upper_bound <= i2.upper_bound
+    if i2.upper_bound is None:
+        if i1.lower_bound is None:
+            return False
+        return i1.lower_bound >= i2.lower_bound
+    if i1.lower_bound is None or i1.upper_bound is None:
+        return False
+    return i1.lower_bound >= i2.lower_bound and i1.upper_bound <= i2.upper_bound
+
+def interval_join(i1: Interval, i2: Interval) -> Interval:
+    if i1.lower_bound is None or i2.lower_bound is None:
+        l = None
+    else:
+        l = min(i1.lower_bound,i2.lower_bound)
+    if i1.upper_bound is None or i2.upper_bound is None:
+        u = None
+    else:
+        u = max(i1.upper_bound,i2.upper_bound)
+    return Interval(l,u)
+
+def interval_meet(i1: Interval, i2: Interval) -> Interval | None:
+    if i1.lower_bound is None:
+        l = i2.lower_bound
+    elif i2.lower_bound is None:
+        l = i1.lower_bound
+    else:
+        l = max(i1.lower_bound,i2.lower_bound)
+    if i1.upper_bound is None:
+        u = i2.upper_bound
+    elif i2.upper_bound is None:
+        u = i1.upper_bound
+    else:
+        u = min(i1.upper_bound,i2.upper_bound)
+    if l is not None and u is not None and l > u: return None
+    return Interval(l,u)
+
+def interval_widen(i1: Interval, i2: Interval) -> Interval:
+    if i1.lower_bound is None or i2.lower_bound is None:
+        l = None
+    elif i1.lower_bound <= i2.lower_bound:
+        l = i1.lower_bound
+    else:
+        l = None
+    if i1.upper_bound is None or i2.upper_bound is None:
+        u = None
+    elif i1.upper_bound >= i2.upper_bound:
+        u = i1.upper_bound
+    else:
+        u = None
+    return Interval(l,u)
+
+def has_strict_positive_val(i: Interval) -> bool:
+    return i.upper_bound is None or i.upper_bound > 0
+
+def has_strict_negative_val(i: Interval) -> bool:
+    return i.lower_bound is None or i.lower_bound < 0
+
+def contains_zero(i: Interval) -> bool:
+    if i.lower_bound is None:
+        return i.upper_bound is None or i.upper_bound >= 0
+    if i.upper_bound is None:
+        return i.lower_bound <= 0
+    return i.lower_bound <= 0 and i.upper_bound >= 0
+
+def is_zero(i: Interval) -> bool:
+    return i.lower_bound == 0 and i.upper_bound == 0
+
+def interval_opp(i: Interval) -> Interval:
+    if i.lower_bound is None:
+        u = None
+    else:
+        u = -i.lower_bound
+    if i.upper_bound is None:
+        l = None
+    else:
+        l = -i.upper_bound
+    return Interval(l,u)
+
+def interval_add(i1: Interval, i2: Interval) -> Interval:
+    raise NotImplementedError
+
+def interval_mul_pos(i1: Interval, i2: Interval) -> Interval:
+    assert (i1.lower_bound is not None and i1.lower_bound >= 0)
+    assert (i2.lower_bound is not None and i2.lower_bound >= 0)
+    l = i1.lower_bound * i2.lower_bound
+    if i1.upper_bound is None or i2.upper_bound is None:
+        u = None
+    else:
+        u = i1.upper_bound * i2.upper_bound
+    return Interval(l,u)
+
+def interval_opt_join(i1: Interval | None, i2: Interval) -> Interval:
+    if i1 is None: return i2
+    return interval_join(i1,i2)
+
+def interval_mul(i1: Interval, i2: Interval) -> Interval:
+    i1_neg = interval_meet(i1,Interval(None,0))
+    i2_neg = interval_meet(i2,Interval(None,0))
+    i1_pos = interval_meet(i1,Interval(0,None))
+    i2_pos = interval_meet(i2,Interval(0,None))
+    if i1_neg is None or i2_pos is None:
+        res = None
+    else:
+        res = interval_opp(interval_mul_pos(interval_opp(i1_neg),i2_pos))
+    if i1_pos is not None and i2_neg is not None:
+        res1 = interval_opp(interval_mul_pos(i1_pos,interval_opp(i2_neg)))
+        res = interval_opt_join(res, res1)
+    if i1_neg is not None and i2_neg is not None:
+        res1 = interval_mul_pos(interval_opp(i1_neg),interval_opp(i2_neg))
+        res = interval_opt_join(res,res1)
+    if i1_pos is not None and i2_pos is not None:
+        res1 = interval_mul_pos(i1_pos, i2_pos)
+        res = interval_opt_join(res, res1)
+    # at least one of the 4 cases is not empty
+    assert res is not None
+    return res
+
+def interval_div(i1: Interval, i2: Interval) -> Interval | Top | None:
+    raise NotImplementedError
+
+def eval_aexp(env: abstract_env, e: ArithExpr) -> Interval | Top | None:
+    """evaluate an arithmetic expression in an abstract environment
+    returns None in case of error
+    """
+    match e:
+        case AECst(value): return Interval(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 or isinstance(res,Top): return res
+            if uop == Uop.OPP: return interval_opp(res)
+            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 isinstance(v1,Top) or isinstance(v2,Top):
+                return Top()
+            match bop:
+                case Bop.ADD: return interval_add(v1,v2)
+                case Bop.MUL: return interval_mul(v1,v2)
+                case Bop.DIV: return interval_div(v1,v2)
+        case _: pass
+
+def eval_bexp(env: abstract_env, e: BoolExpr) -> bool | BTop | None:
+    """abstract evaluation of a boolean expression"""
+    match e:
+        case BEPlain(aexpr):
+            res = eval_aexp(env, aexpr)
+            if res is None: return None
+            if isinstance(res,Top): return BTop(True)
+            if res.lower_bound == 0 and res.upper_bound == 0: return True
+            if not interval_leq(Interval(0,0), res): return False
+            return BTop(False)
+        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 isinstance(v1,Top) or isinstance(v2,Top): return BTop(True)
+            if v1.lower_bound is None or v2.lower_bound is None or v1.upper_bound is None or v2.upper_bound is None:
+                return BTop(False)
+            if v1.lower_bound > v2.upper_bound or v1.upper_bound < v2.lower_bound:
+                return False
+            if v1.lower_bound == v1.upper_bound and v2.lower_bound == v2.upper_bound and v1.lower_bound == v2.lower_bound:
+                return True
+            return BTop(False)
+        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 isinstance(v1,Top) or isinstance(v2,Top): return BTop(True)
+            if v1.upper_bound is None:
+                if v1.lower_bound is None:
+                    return BTop(False)
+                if v2.upper_bound is None:
+                    return BTop(False)
+                if v2.upper_bound < v1.lower_bound:
+                    return False
+                return BTop(False)
+            if v2.lower_bound is None:
+                return BTop(False)
+            if v1.upper_bound <= v2.lower_bound:
+                return True
+            if v1.lower_bound is None:
+                return BTop(False)
+            if v2.upper_bound is None:
+                return BTop(False)
+            if v2.upper_bound < v1.lower_bound:
+                return False
+            return BTop(False)
+        case BENeg(expr):
+            v = eval_bexp(env,expr)
+            if v is None: return None
+            if isinstance(v,BTop): return v
+            return not v
+        case _: pass
+
+def reduce_eq(s:state, x:str, i: Interval) -> state:
+    raise NotImplementedError
+
+def reduce_neq(s:state, x: str, i: Interval) -> state:
+    raise NotImplementedError
+
+def reduce_leq(s:state,x:str,upper_bound: int) -> state:
+    raise NotImplementedError
+
+def reduce_geq(s: state, x: str, lower_bound: int) -> state:
+    raise NotImplementedError
+
+def reduce_state(s: state,c: BoolExpr) -> state:
+    if s is None: return s
+    match c:
+        case BEEq(AEVar(x), AEVar(y)):
+            vx = eval_aexp(s,AEVar(x))
+            vy = eval_aexp(s,AEVar(y))
+            if vx is None or vy is None: return None
+            if not isinstance(vx,Top):
+                s = reduce_eq(s,y,vx)
+            if not isinstance(vy,Top):
+                s = reduce_eq(s,x,vy)
+            return s
+        case BEEq(AEVar(x), right_expr):
+            v = eval_aexp(s,right_expr)
+            if v is None: return None
+            if isinstance(v,Top): return s
+            return reduce_eq(s,x,v)
+        case BEEq(left_expr, AEVar(y)):
+            v = eval_aexp(s,left_expr)
+            if v is None: return None
+            if isinstance(v,Top): return s
+            return reduce_eq(s,y,v)
+        case BELeq(AEVar(x), AEVar(y)):
+            vx = eval_aexp(s,AEVar(x))
+            vy = eval_aexp(s,AEVar(y))
+            if vx is None or vy is None: return None
+            if not isinstance(vy,Top) and vy.upper_bound is not None:
+                s = reduce_leq(s,x,vy.upper_bound)
+            if not isinstance(vx,Top) and vx.lower_bound is not None:
+                s = reduce_geq(s,y,vx.lower_bound)
+            return s
+        case BELeq(AEVar(x),right_expr):
+            v = eval_aexp(s,right_expr)
+            if v is None: return None
+            if isinstance(v,Top) or v.upper_bound is None: return s
+            return reduce_leq(s,x,v.upper_bound)
+        case BELeq(left_expr,AEVar(y)):
+            v = eval_aexp(s,left_expr)
+            if v is None: return None
+            if isinstance(v,Top) or v.lower_bound is None: return s
+            return reduce_geq(s,y,v.lower_bound)
+        case BENeg(BEEq(AEVar(x), AEVar(y))):
+            vx = eval_aexp(s,AEVar(x))
+            vy = eval_aexp(s,AEVar(y))
+            if vx is None or vy is None: return None
+            if not isinstance(vx,Top):
+                s = reduce_neq(s,y,vx)
+            if not isinstance(vy,Top):
+                s = reduce_neq(s,x,vy)
+            return s
+        case BENeg(BEEq(AEVar(x), right_expr)):
+            v = eval_aexp(s,right_expr)
+            if v is None: return None
+            if isinstance(v,Top): return s
+            return reduce_neq(s,x,v)
+        case BENeg(BEEq(left_expr, AEVar(y))):
+            v = eval_aexp(s,left_expr)
+            if v is None: return None
+            if isinstance(v,Top): return s
+            return reduce_neq(s,y,v)
+        case BENeg(BELeq(AEVar(x), AEVar(y))):
+            vx = eval_aexp(s,AEVar(x))
+            vy = eval_aexp(s,AEVar(y))
+            if vx is None or vy is None: return None
+            if not isinstance(vx,Top) and vx.upper_bound is not None:
+                s = reduce_leq(s,y,vx.upper_bound - 1)
+            if not isinstance(vy,Top) and vy.lower_bound is not None:
+                s = reduce_geq(s,x,vy.lower_bound + 1)
+            return s
+        case BENeg(BELeq(AEVar(x),right_expr)):
+            v = eval_aexp(s,right_expr)
+            if v is None: return None
+            if isinstance(v,Top) or v.lower_bound is None: return s
+            return reduce_geq(s,x,v.lower_bound + 1)
+        case BENeg(BELeq(left_expr,AEVar(y))):
+            v = eval_aexp(s,left_expr)
+            if v is None: return None
+            if isinstance(v,Top) or v.upper_bound is None: return s
+            return reduce_leq(s,y,v.upper_bound - 1)
+        case _: return s
+
+class Interval_analysis(Transfer[state]):
+    variables: frozenset[str]
+    def __init__(self,instr: Instr):
+        self.variables = variables_of_instr(instr)
+    def bottom(self) -> state:
+        return None
+    def init_state(self) -> state:
+        return dict.fromkeys(self.variables, Interval(None,None))
+    def join(self,s1:state,s2:state) -> state:
+        if s1 is None: return s2
+        if s2 is None: return s1
+        res: abstract_env = {}
+        for var in self.variables:
+            v1 = s1[var]
+            v2 = s2[var]
+            v = interval_join(v1,v2)
+            res[var] = v
+        return res
+
+    def widen(self, s1:state, s2:state) -> state:
+        raise NotImplementedError
+
+    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:
+            if not interval_leq(s1[var],s2[var]): return False
+        return True
+
+    def tr_skip(self,s: state) -> state:
+        return s
+
+    def tr_set(self,s: state,v: str,e: ArithExpr) -> state:
+        raise NotImplementedError
+
+    def tr_test(self,s: state,c: BoolExpr) -> state:
+        raise NotImplementedError
+
+    def tr_err(self,s: state,e: Expr) -> state:
+        raise NotImplementedError
+
+def analyze(i: Instr) -> None:
+    cfg = Cfg(i)
+    res = fixpoint_iteration(Interval_analysis(i), cfg)
+    for node in cfg.g.nodes:
+        print(f"State at {node}:")
+        s = res[node]
+        if s is not None:
+            for (v, s) in s.items():
+                print(f"  {v}: {s}")
diff --git a/Pystan/config/sign_analysis.py b/Pystan/config/sign_analysis.py
new file mode 100644
index 0000000..61bd064
--- /dev/null
+++ b/Pystan/config/sign_analysis.py
@@ -0,0 +1,338 @@
+# pyright: strict
+"""
+implements a sign analysis
+"""
+from enum import Enum
+from dataclasses import dataclass
+from cfg import Cfg
+from iteration import Transfer, fixpoint_iteration
+from syntax import *
+
+class sign(Enum):
+    SNEG = auto()
+    SPOS = auto()
+    Z = auto()
+    NEG = auto()
+    POS = auto()
+    NZ = auto()
+    INT = auto()
+
+class Top:
+    def __str__(self): return "TOP"
+
+@dataclass
+class BTop:
+    """class used for the evaluation of boolean expressions
+
+    BTop(False) indicates that we don't know if the result is True or False, but
+    that the evaluation cannot lead to an error
+    BTop(True) indicates that we neither know the result nor whether an error can occur
+    """
+    has_error: bool
+    def __str__(self):
+        if self.has_error: return "TOP (maybe error)"
+        else: return "TOP (no error)"
+
+type abstract_env = dict[str, sign]
+"""mapping from variables to abstract values.
+
+As with concrete environment, a variable not in the dictionary will
+lead to an error if we try to obtain its value
+"""
+type state = abstract_env | None
+"""abstract state is either an abstract env or bottom
+"""
+
+def sign_leq(v1: sign, v2:sign) -> bool:
+    """order relation on the sign lattice"""
+    if v1 == v2: return True
+    match v1,v2:
+        case _, sign.INT: return True
+        case sign.INT, _: return False
+        case sign.SNEG,(sign.NEG | sign.NZ): return True
+        case sign.SPOS,(sign.POS | sign.NZ): return True
+        case sign.Z,(sign.POS | sign.NEG): return True
+        case sign.NEG,sign.NZ: return True
+        case sign.POS,sign.NZ: return True
+        case _: return False
+
+def sign_join(v1: sign, v2:sign) -> sign:
+    """computes least upper bound"""
+    match v1,v2:
+        case sign.INT, _: return sign.INT
+        case _, sign.INT: return sign.INT
+        case sign.NZ, (sign.NZ | sign.SPOS | sign.SNEG): return sign.NZ
+        case sign.NZ,_: return sign.INT
+        case sign.NEG, (sign.NEG | sign.SNEG | sign.Z): return sign.NEG
+        case sign.NEG, _: return sign.INT
+        case sign.POS, (sign.POS | sign.SPOS | sign.Z): return sign.POS
+        case sign.POS,_: return sign.INT
+        case sign.SNEG, sign.SNEG: return sign.SNEG
+        case sign.SNEG, (sign.Z | sign.NEG): return sign.NEG
+        case sign.SNEG, (sign.SPOS | sign.NZ): return sign.NZ
+        case sign.SNEG, _: return sign.INT
+        case sign.SPOS, sign.SPOS: return sign.SPOS
+        case sign.SPOS, (sign.POS | sign.Z): return sign.POS
+        case sign.SPOS, (sign.SNEG | sign.NZ): return sign.NZ
+        case sign.SPOS, _: return sign.INT
+        case sign.Z, sign.SNEG: return sign.NEG
+        case sign.Z, sign.SPOS: return sign.POS
+        case sign.Z, sign.NZ: return sign.INT
+        case sign.Z, _: return v2
+
+def sign_meet(s1: sign, s2:sign) -> sign | None:
+    """computes greatest lower bound. Can be None (i.e. bottom)"""
+    match s1,s2:
+        case sign.INT, _: return s2
+        case _, sign.INT: return s1
+        case sign.SNEG, (sign.NEG | sign.NZ | sign.SNEG): return s1
+        case (sign.NEG | sign.NZ | sign.SNEG), sign.SNEG: return s2
+        case sign.SNEG, _: return None
+        case _, sign.SNEG: return None
+        case sign.SPOS, (sign.POS | sign.NZ | sign.SPOS): return s1
+        case (sign.POS | sign.NZ | sign.SPOS), sign.SPOS: return s2
+        case sign.SPOS, _: return None
+        case _, sign.SPOS: return None
+        case sign.Z, (sign.NEG | sign.POS): return s1
+        case (sign.NEG | sign.POS), sign.Z: return s2
+        case sign.Z, sign.Z: return s1
+        case sign.Z, _: return None
+        case _, sign.Z: return None
+        case sign.NEG, sign.NEG: return s1
+        case sign.POS, sign.POS: return s1
+        case sign.NEG, sign.POS: return sign.Z
+        case sign.POS, sign.NEG: return sign.Z
+        case sign.NZ, sign.NEG: return sign.SNEG
+        case sign.NEG, sign.NZ: return sign.SNEG
+        case sign.NZ, sign.POS: return sign.SPOS
+        case sign.POS, sign.NZ: return sign.SPOS
+        case sign.NZ, sign.NZ: return sign.NZ
+
+def sign_opp(s:sign) -> sign:
+    """unary minus"""
+    match s:
+        case sign.SNEG: return sign.SPOS
+        case sign.SPOS: return sign.SNEG
+        case sign.POS: return sign.NEG
+        case sign.NEG: return sign.POS
+        case _: return s
+
+def sign_add(s1:sign,s2:sign) -> sign:
+    """addition"""
+    match (s1,s2):
+        case sign.INT,_: return sign.INT
+        case _,sign.INT: return sign.INT
+        case sign.Z,_: return s2
+        case _,sign.Z: return s1
+        case sign.SNEG, (sign.SNEG | sign.NEG): return sign.SNEG
+        case (sign.SNEG | sign.NEG), sign.SNEG: return sign.SNEG
+        case sign.NEG, sign.NEG: return sign.NEG
+        case sign.SPOS, (sign.SPOS | sign.POS): return sign.SPOS
+        case (sign.SPOS | sign.POS), sign.SPOS: return sign.SPOS
+        case sign.POS, sign.POS: return sign.POS
+        case _: return sign.INT
+
+def sign_mul(s1: sign, s2: sign) -> sign:
+    """multiplication"""
+    match s1,s2:
+        case sign.INT,_: return sign.INT
+        case _,sign.INT: return sign.INT
+        case sign.Z,_: return sign.Z
+        case _,sign.Z: return sign.Z
+        case sign.SPOS, _: return s2
+        case _, sign.SPOS: return s1
+        case sign.SNEG, _: return sign_opp(s2)
+        case _,sign.SNEG: return sign_opp(s1)
+        case sign.NZ, _: return sign.INT
+        case _, sign.NZ: return sign.INT
+        case sign.NEG, sign.NEG: return sign.POS
+        case sign.NEG, sign.POS: return sign.NEG
+        case sign.POS, sign.NEG: return sign.NEG
+        case sign.POS, sign.POS: return sign.POS
+
+def sign_div(s1: sign, s2: sign) -> sign | Top | None:
+    """division: can lead to errors even if operands are plain sign"""
+    match s1, s2:
+        case _,sign.Z: return None
+        case _,sign.INT: return Top()
+        case _,sign.POS: return Top()
+        case _,sign.NEG: return Top()
+        case sign.Z,_: return sign.Z
+        case sign.INT,_: return sign.INT
+        case sign.NZ,_: return sign.INT
+        case _,sign.NZ: return sign.INT
+        case (sign.POS | sign.SPOS), sign.SPOS: return sign.POS
+        case (sign.NEG | sign.SNEG), sign.SPOS: return sign.NEG
+        case (sign.POS | sign.SPOS), sign.SNEG: return sign.NEG
+        case (sign.NEG | sign.SNEG), sign.SNEG: return sign.POS
+
+def eval_aexp(env: abstract_env, e: ArithExpr) -> sign | Top | None:
+    """evaluate an arithmetic expression in an abstract environment
+    returns None in case of error
+    """
+    match e:
+        case AECst(value):
+            raise NotImplementedError
+        case AEVar(var):
+            raise NotImplementedError
+        case AEUop(uop,expr):
+            raise NotImplementedError
+        case AEBop(bop,left_expr,right_expr):
+            raise NotImplementedError
+        case _: pass
+
+def eval_bexp(env: abstract_env, e: BoolExpr) -> bool | BTop | None:
+    """abstract evaluation of a boolean expression"""
+    match e:
+        case BEPlain(aexpr):
+            raise NotImplementedError
+        case BEEq(left_expr,right_expr):
+            raise NotImplementedError
+        case BELeq(left_expr,right_expr):
+            raise NotImplementedError
+        case BENeg(expr):
+            raise NotImplementedError
+        case _: pass
+
+def reduce_eq_sign(s: state, x: str, expr: ArithExpr) -> state:
+    """reduce the value associated to x in s under the hypothesis that x == expr"""
+    if s is None: return None
+    v = eval_aexp(s,expr)
+    if v is None: return None
+    if isinstance(v,Top): return s
+    res = sign_meet(s[x],v)
+    if res is None: return None
+    return s | { x: res }
+
+def reduce_neq_sign(s: state, x: str, expr: ArithExpr) -> state:
+    """reduce the value associated to x in s under the hypothesis that x != expr"""
+    if s is None: return None
+    v = eval_aexp(s,expr)
+    if v is None: return None
+    if isinstance(v,Top): return s
+    match s[x],v:
+        case sign.POS, sign.Z: return s | { x: sign.SPOS }
+        case sign.NEG, sign.Z: return s | { x: sign.SNEG }
+        case sign.INT, sign.Z: return s | { x: sign.NZ }
+        case _: return s
+
+def reduce_upper_bound(s: state, x: str, expr: ArithExpr) -> state:
+    """reduce the value associated to x in s under the hypothesis that x <= expr"""
+    if s is None: return None
+    v = eval_aexp(s,expr)
+    if v is None: return None
+    if isinstance(v,Top): return s
+    upper = sign.INT
+    match v:
+        case sign.NEG | sign.SNEG:
+            upper = v
+        case sign.Z:
+            upper = sign.NEG
+        case _: pass
+    res = sign_meet(s[x],upper)
+    if res is None: return None
+    return s | { x: res }
+
+def reduce_strict_upper_bound(s: state, x: str, expr: ArithExpr) -> state:
+    """reduce the value associated to x in s under the hypothesis that x < expr"""
+    if s is None: return None
+    v = eval_aexp(s,expr)
+    if v is None: return None
+    if isinstance(v,Top): return s
+    match v:
+        case sign.NEG | sign.SNEG | sign.Z:
+            upper = sign.SNEG
+        case _:
+            upper = sign.INT
+    res = sign_meet(s[x],upper)
+    if res is None: return None
+    return s | { x: res }
+
+def reduce_lower_bound(s: state, x: str, expr: ArithExpr) -> state:
+    """reduce the value associated to x in s under the hypothesis that x >= expr"""
+    if s is None: return None
+    v = eval_aexp(s,expr)
+    if v is None: return None
+    if isinstance(v,Top): return s
+    lower = sign.INT
+    match v:
+        case sign.POS | sign.SPOS:
+            lower = v
+        case sign.Z:
+            lower = sign.POS
+        case _: pass
+    res = sign_meet(s[x],lower)
+    if res is None: return None
+    return s | { x: res }
+
+def reduce_strict_lower_bound(s: state, x: str, expr: ArithExpr) -> state:
+    """reduce the value associated to x in s under the hypothesis that x > expr"""
+    if s is None: return s
+    v = eval_aexp(s,expr)
+    if v is None: return None
+    if isinstance(v,Top): return s
+    match v:
+        case sign.POS | sign.SPOS | sign.Z:
+            lower = sign.SPOS
+        case _:
+            lower = sign.INT
+    res = sign_meet(s[x],lower)
+    if res is None: return None
+    return s | { x: res }
+
+def reduce_state(s: state,c: BoolExpr) -> state:
+    if s is None: return s
+    match c:
+        # To be completed (see course's slides)
+        case _: return s
+
+class Sign_interp(Transfer[state]):
+    variables: frozenset[str]
+    def __init__(self,instr: Instr):
+        self.variables = variables_of_instr(instr)
+    def bottom(self) -> state:
+        return None
+    def init_state(self) -> state:
+        return dict.fromkeys(self.variables, sign.INT)
+    def join(self,s1:state,s2:state) -> state:
+        if s1 is None: return s2
+        if s2 is None: return s1
+        res: abstract_env = {}
+        for var in self.variables:
+            v1 = s1[var]
+            v2 = s2[var]
+            res[var] = sign_join(v1,v2)
+        return res
+
+    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:
+            if not sign_leq(s1[var], s2[var]): return False
+        return True
+
+    def tr_skip(self,s: state) -> state:
+        return s
+
+    def tr_set(self,s: state,v: str,e: ArithExpr) -> state:
+        raise NotImplementedError
+
+    def tr_test(self,s: state,c: BoolExpr) -> state:
+        raise NotImplementedError
+
+    def tr_err(self,s: state,e: Expr) -> state:
+        if s is None: return s
+        if isinstance(e,ArithExpr):
+            raise NotImplementedError
+        if isinstance(e,BoolExpr):
+            raise NotImplementedError
+
+def analyze(i: Instr) -> None:
+    cfg = Cfg(i)
+    res = fixpoint_iteration(Sign_interp(i), cfg)
+    for node in cfg.g.nodes:
+        print(f"State at {node}:")
+        s = res[node]
+        if s is not None:
+            for (v, s) in s.items():
+                print(f"  {v}: {s}")
-- 
GitLab