-- ExprEval/Basic.lean

import ExprEval.Steps

import ExprEval.Lemmas

variable (V: Type)

-- Repository definitions used:
content: def BoolStepStar (V: Type) := StepStar (Step := BoolStep V) V

content: inductive BoolStep: (val: V -> Int) -> (BoolExpr V) -> (BoolExpr V) -> Prop where
    | notIsBoolNot (b: Bool): BoolStep val
        (BoolExpr.Not (BoolExpr.Const b))
        (BoolExpr.Const !b)
    | andLeftTrue (e: BoolExpr V): BoolStep val
        (BoolExpr.And (BoolExpr.Const true) e)
        e
    | orLeftFalse (e: BoolExpr V): BoolStep val
        (BoolExpr.Or (BoolExpr.Const false) e)
        e
    | andLeftShortCircuit e : BoolStep val
        (BoolExpr.And (BoolExpr.Const false) e)
        (BoolExpr.Const false)
    | orLeftShortCircuit e : BoolStep val
        (BoolExpr.Or (BoolExpr.Const true) e)
        (BoolExpr.Const true)
    | lessConstConstTrue n₁ n₂ : n₁ < n₂ -> BoolStep val
        (BoolExpr.Less (ArExpr.Const n₁) (ArExpr.Const n₂))
        (BoolExpr.Const true)
    | lessConstConstFalse n₁ n₂ : n₁ >= n₂ -> BoolStep val
        (BoolExpr.Less (ArExpr.Const n₁) (ArExpr.Const n₂))
        (BoolExpr.Const false)
    | eqConstConstTrue n₁ n₂ : n₁ = n₂ -> BoolStep val
        (BoolExpr.Eq (ArExpr.Const n₁) (ArExpr.Const n₂))
        (BoolExpr.Const true)
    | eqConstConstFalse n₁ n₂ : n₁ != n₂ -> BoolStep val
        (BoolExpr.Eq (ArExpr.Const n₁) (ArExpr.Const n₂))
        (BoolExpr.Const false)
    | lessArStepLeft (e₁ e₁' e₂: ArExpr V):
        ArStep val e₁ e₁' ->
            BoolStep val
                (BoolExpr.Less e₁ e₂)
                (BoolExpr.Less e₁' e₂)
    | eqArStepLeft (e₁ e₁' e₂: ArExpr V):
        ArStep val e₁ e₁' ->
            BoolStep val
                (BoolExpr.Eq e₁ e₂)
                (BoolExpr.Eq e₁' e₂)
     | lessArStepRight (e₁ e₂ e₂': ArExpr V):
        ArStep val e₂ e₂' ->
            BoolStep val
                (BoolExpr.Less e₁ e₂)
                (BoolExpr.Less e₁ e₂')
    | eqArStepRight (e₁ e₂ e₂': ArExpr V):
        ArStep val e₂ e₂' ->
            BoolStep val
                (BoolExpr.Eq e₁ e₂)
                (BoolExpr.Eq e₁ e₂')
    | orStepLeft (e₁ e₁' e₂: BoolExpr V):
        BoolStep val e₁ e₁' ->
            BoolStep val
                (BoolExpr.Or e₁ e₂)
                (BoolExpr.Or e₁' e₂)
    | andStepLeft (e₁ e₁' e₂: BoolExpr V):
        BoolStep val e₁ e₁' ->
            BoolStep val
                (BoolExpr.And e₁ e₂)
                (BoolExpr.And e₁' e₂)
    | notStep (e e' : BoolExpr V):
        BoolStep val e e' -> BoolStep val
            (BoolExpr.Not e)
            (BoolExpr.Not e')

content: inductive ArStep: (val: V -> Int) -> (ArExpr V) -> (ArExpr V) -> Prop where
    | getVarValue (var: V) :
        ArStep
            val
            (ArExpr.Var var)
            (ArExpr.Const (val var))
    | addConstConst(n₁ n₂: Int) :
        ArStep
            val
            (ArExpr.Add (ArExpr.Const n₁) (ArExpr.Const n₂))
            (ArExpr.Const (n₁ + n₂))
    | subConstConst(n₁ n₂: Int) :
        ArStep
            val
            (ArExpr.Sub (ArExpr.Const n₁) (ArExpr.Const n₂))
            (ArExpr.Const (n₁ - n₂))
    | mulConstConst(n₁ n₂: Int) :
        ArStep
            val
            (ArExpr.Mul (ArExpr.Const n₁) (ArExpr.Const n₂))
            (ArExpr.Const (n₁ * n₂))
    | addLeft (e₁ e₁' e₂) : ArStep val e₁ e₁' -> ArStep val (ArExpr.Add e₁ e₂) (ArExpr.Add e₁' e₂)

    | subLeft (e₁ e₁' e₂) : ArStep val e₁ e₁' -> ArStep val (ArExpr.Sub e₁ e₂) (ArExpr.Sub e₁' e₂)

    | mulLeft (e₁ e₁' e₂) : ArStep val e₁ e₁' -> ArStep val (ArExpr.Mul e₁ e₂) (ArExpr.Mul e₁' e₂)

    | addRight (n: Int) (e₂ e₂': ArExpr V) : ArStep val e₂ e₂' -> ArStep val (ArExpr.Add (ArExpr.Const n) e₂)  (ArExpr.Add (ArExpr.Const n) e₂')

    | subRight (n: Int) (e₂ e₂': ArExpr V) : ArStep val e₂ e₂' -> ArStep val (ArExpr.Sub (ArExpr.Const n) e₂)  (ArExpr.Sub (ArExpr.Const n) e₂')
    | mulRight (n: Int) (e₂ e₂': ArExpr V) : ArStep val e₂ e₂' -> ArStep val (ArExpr.Mul (ArExpr.Const n) e₂)  (ArExpr.Mul (ArExpr.Const n) e₂')

    | ifStep (e e': BoolExpr V) (a b : ArExpr V): BoolStep val e e' ->
        ArStep val (ArExpr.If e a b) (ArExpr.If e' a b)
    | ifCondTrue (a b: ArExpr V) : ArStep val (ArExpr.If (BoolExpr.Const true) a b) a
    | ifCondFalse (a b: ArExpr V) : ArStep val (ArExpr.If (BoolExpr.Const false) a b) b

    inductive ArExpr: Type
        | Const: Int -> ArExpr
        | Add: ArExpr -> ArExpr -> ArExpr
        | Sub: ArExpr -> ArExpr -> ArExpr
        | Mul: ArExpr -> ArExpr -> ArExpr
        | Var: V -> ArExpr
        | If : BoolExpr -> ArExpr -> ArExpr -> ArExpr

    inductive BoolExpr : Type
        | Const: Bool -> BoolExpr
        | Less: ArExpr -> ArExpr -> BoolExpr
        | Eq: ArExpr -> ArExpr -> BoolExpr
        | Not : BoolExpr -> BoolExpr
        | And : BoolExpr -> BoolExpr -> BoolExpr
        | Or : BoolExpr -> BoolExpr -> BoolExpr

content: inductive StepStar {ExprType: Type} {Step: StepKind V ExprType} : (val: V -> Int) -> ExprType -> ExprType -> Prop where
        | refl val e : StepStar val e e
        | trans e₁ e₂ e₃ : Step val e₁ e₂ -> StepStar val e₂ e₃ -> StepStar val e₁ e₃

content: def BoolStepStar.refl {V: Type} (val: V -> Int) := StepStar.refl (Step := BoolStep V) val

    def eval (val: V -> Int) (e: ArExpr V) : Int :=
        match e with
            | ArExpr.Const x => x
            | ArExpr.Add lhs rhs => (eval val lhs) + (eval val rhs)
            | ArExpr.Sub lhs rhs => (eval val lhs) - (eval val rhs)
            | ArExpr.Mul lhs rhs => (eval val lhs) * (eval val rhs)
            | ArExpr.Var v => val v
            | ArExpr.If cond then_e else_e => if eval_bool val cond then eval val then_e else eval val else_e

    def eval_bool (val: V -> Int) (e: BoolExpr V): Bool :=
        match e with
        | BoolExpr.Const b => b
        | BoolExpr.Less l r =>  (eval val l) < (eval val r)
        | BoolExpr.Eq l r => (eval val l) == (eval val r)
        | BoolExpr.Not e => not (eval_bool val e)
        | BoolExpr.And l r => if eval_bool val l then eval_bool val r else false
        | BoolExpr.Or l r => if eval_bool val l then true else eval_bool val r

content: def ArStepStar.trans {V: Type} {val: V -> Int} e₁ e₂ e₃ := StepStar.trans e₁ e₂ e₃ (Step := ArStep V) (val := val)

content: def ArStepStar (V: Type) := StepStar (Step := ArStep V) V

content: def ArStepStar.refl {V: Type} (val: V -> Int) := StepStar.refl (Step := ArStep V) val

content: def BoolStepStar.trans {V: Type} {val: V -> Int} e₁ e₂ e₃ := StepStar.trans e₁ e₂ e₃ (Step := BoolStep V) (val := val)

content: def StepKind (V: Type) (ExprType: Type):= (V -> Int) -> ExprType -> ExprType -> Prop

-- Repository lemmas used:
content: theorem BoolStepStar.andStepLeft (e₁ e₁' e₂: BoolExpr V):
        BoolStepStar V val e₁ e₁' -> BoolStepStar V val
            (BoolExpr.And e₁ e₂)
            (BoolExpr.And e₁' e₂)

content: theorem chasles_step_star {ExprKind: Type} {Step: StepKind V ExprKind} {e₁ e₂ e₃: ExprKind}:
    StepStar (Step := Step) V val e₁ e₂  ->
        StepStar (Step := Step) V val e₂ e₃ ->
            StepStar (Step := Step) V val e₁ e₃

content: theorem BoolStepStar.lessArStepStarLeft (e₁ e₁' e₂: ArExpr V):
        ArStepStar V val e₁ e₁' ->
            BoolStepStar V val
                (BoolExpr.Less e₁ e₂)
                (BoolExpr.Less e₁' e₂)

content: theorem BoolStepStar.notStep {V: Type} {val: V -> Int}
        (e e': BoolExpr V):
            BoolStepStar V val e e' -> BoolStepStar V val (BoolExpr.Not e) (BoolExpr.Not e')

content: theorem BoolStepStar.lessArStepStarRight (e₁ e₂ e₂': ArExpr V):
        ArStepStar V val e₂ e₂' ->
            BoolStepStar V val
                (BoolExpr.Less e₁ e₂)
                (BoolExpr.Less e₁ e₂')

content: theorem step_to_stepstar {ExprKind: Type} {Step: StepKind V ExprKind} {e e': ExprKind}:
    Step val e e' -> StepStar (Step := Step) V val e e'

content: theorem BoolStepStar.eqArStepStarRight (e₁ e₂ e₂': ArExpr V):
        ArStepStar V val e₂ e₂' ->
            BoolStepStar V val
                (BoolExpr.Eq e₁ e₂)
                (BoolExpr.Eq e₁ e₂')

content: theorem BoolStepStar.eqArStepStarLeft (e₁ e₁' e₂: ArExpr V):
        ArStepStar V val e₁ e₁' ->
            BoolStepStar V val
                (BoolExpr.Eq e₁ e₂)
                (BoolExpr.Eq e₁' e₂)

content: theorem BoolStepStar.orStepLeft (e₁ e₁' e₂: BoolExpr V):
        BoolStepStar V val e₁ e₁' -> BoolStepStar V val
            (BoolExpr.Or e₁ e₂)
            (BoolExpr.Or e₁' e₂)

content: theorem arstepstar_add_right (n: Int) (e₂ e₂': ArExpr V) :
            ArStepStar V val e₂ e₂' ->
                    ArStepStar V val
                            (ArExpr.Add (ArExpr.Const n) e₂)
                            (ArExpr.Add (ArExpr.Const n) e₂')

content: theorem arstepstar_sub_left (e₁ e₁' e₂: ArExpr V) :
            ArStepStar V val e₁ e₁' ->
                    ArStepStar V val
                            (ArExpr.Sub e₁ e₂)
                            (ArExpr.Sub e₁' e₂)

content: theorem arstepstar_add_left (e₁ e₁' e₂: ArExpr V) :
            ArStepStar V val e₁ e₁' -> ArStepStar V val (ArExpr.Add e₁ e₂) (ArExpr.Add e₁' e₂)

content: theorem arstepstar_sub_right (n: Int) (e₂ e₂': ArExpr V) :
            ArStepStar V val e₂ e₂' ->
                    ArStepStar V val
                            (ArExpr.Sub (ArExpr.Const n) e₂)
                            (ArExpr.Sub (ArExpr.Const n) e₂')

content: theorem ArStepStar.ifStep (e e': BoolExpr V) (a b: ArExpr V): BoolStepStar V val e e' ->
        ArStepStar V val (ArExpr.If e a b) (ArExpr.If e' a b)

content: theorem arstepstar_mul_right (n: Int) (e₂ e₂': ArExpr V) :
            ArStepStar V val e₂ e₂' ->
                    ArStepStar V val
                            (ArExpr.Mul (ArExpr.Const n) e₂)
                            (ArExpr.Mul (ArExpr.Const n) e₂')

content: theorem arstepstar_mul_left (e₁ e₁' e₂: ArExpr V) :
            ArStepStar V val e₁ e₁' ->
                    ArStepStar V val
                            (ArExpr.Mul e₁ e₂)
                            (ArExpr.Mul e₁' e₂)

-- Local definitions used:

-- Local lemmas used:
content:     theorem eval_completeness (e: ArExpr V) : ArStepStar V val e (ArExpr.Const (eval V val e))

-- Target theorem and proof:
theorem eval_bool_completeness (e: BoolExpr V): BoolStepStar V val e (BoolExpr.Const (eval_bool V val e))
:= by
        cases e with
        | Const b =>
            simp [eval_bool]
            apply BoolStepStar.refl
        | Not e =>
            simp [eval_bool]
            have ihn := eval_bool_completeness e (val := val)
            have h := BoolStepStar.notStep e _ ihn
            have h1 := BoolStep.notIsBoolNot (eval_bool V val e) (val := val)
            have h2 := (step_to_stepstar V h1)
            apply chasles_step_star V h h2
        | And l r =>
            have l_step := eval_bool_completeness l (val := val)
            have r_step := eval_bool_completeness r (val := val)
            simp [eval_bool]
            by_cases h: (eval_bool V val l)
            .   simp [and]
                rw [h]
                simp
                rw [h] at l_step
                have h1 := BoolStep.andLeftTrue r (val := val)
                have h2 := StepStar.trans _ _ _ h1 r_step
                have h3 := BoolStepStar.andStepLeft _ _ r l_step
                exact chasles_step_star V h3 h2

            .   replace h := eq_false_of_ne_true h
                rw [h]
                rw [h] at l_step
                simp [and]
                have h1 := BoolStepStar.andStepLeft _ _ r l_step
                have h2 := BoolStep.andLeftShortCircuit r (val := val)
                have h3 := step_to_stepstar V h2
                exact chasles_step_star V h1 h3
        | Or l r =>
            have l_step := eval_bool_completeness l (val := val)
            have r_step := eval_bool_completeness r (val := val)
            simp [eval_bool]
            by_cases h: (eval_bool V val l)
            .   rw [h]
                rw [h] at l_step
                simp [or]
                have h1 := BoolStepStar.orStepLeft _ _ r l_step
                have h2 := BoolStep.orLeftShortCircuit r (val := val)
                have h3 := step_to_stepstar V h2
                exact chasles_step_star V h1 h3

            .   replace h := eq_false_of_ne_true h
                rw [h]
                rw [h] at l_step
                simp
                have h1 := BoolStep.orLeftFalse r (val := val)
                have h2 := StepStar.trans _ _ _ h1 r_step
                have h3 := BoolStepStar.orStepLeft _ _ r l_step
                exact chasles_step_star V h3 h2
        | Eq l r =>
            simp [eval_bool]

            by_cases h: (eval V val l == eval V val r)

            .   replace h := eq_of_beq h
                rw [h]
                simp
                have h1 := BoolStep.eqConstConstTrue _ _ h (val := val)
                have hl := eval_completeness l (val := val)
                have hr := eval_completeness r (val := val)
                have h2 := BoolStepStar.eqArStepStarLeft l (ArExpr.Const (eval V val l)) r hl
                have h3 := BoolStepStar.eqArStepStarRight (ArExpr.Const (eval V val l)) r (ArExpr.Const (eval V val r)) hr
                have h4 := chasles_step_star V h2 h3
                exact chasles_step_star V h4 (step_to_stepstar V h1)

            .   replace h := eq_false_of_ne_true h
                rw [h]
                have h' := ne_of_beq_false h
                rw [<- bne_iff_ne] at h'
                have h1 := BoolStep.eqConstConstFalse _ _ h' (val := val)
                have hl := eval_completeness l (val := val)
                have hr := eval_completeness r (val := val)
                have h2 := BoolStepStar.eqArStepStarLeft l (ArExpr.Const (eval V val l)) r hl
                have h3 := BoolStepStar.eqArStepStarRight (ArExpr.Const (eval V val l)) r (ArExpr.Const (eval V val r)) hr
                have h4 := chasles_step_star V h2 h3
                exact chasles_step_star V h4 (step_to_stepstar V h1)
        | Less l r =>
            simp [eval_bool]

            by_cases h: (eval V val l < eval V val r)

            .   rw [decide_eq_true]
                have h1 := BoolStep.lessConstConstTrue _ _ h (val := val)
                have hr := eval_completeness r (val := val)
                have hl := eval_completeness l (val := val)
                have hr := eval_completeness r (val := val)
                have h2 := BoolStepStar.lessArStepStarLeft l (ArExpr.Const (eval V val l)) r hl
                have h3 := BoolStepStar.lessArStepStarRight (ArExpr.Const (eval V val l)) r (ArExpr.Const (eval V val r)) hr
                have h4 := chasles_step_star V h2 h3
                apply chasles_step_star V h4 (step_to_stepstar V h1)
                exact h

            .   rw [decide_eq_false]
                have h' := h
                rw [Int.not_lt] at h'
                rw [<- ge_iff_le] at h'
                have h1 := BoolStep.lessConstConstFalse _ _ h' (val := val)
                have hl := eval_completeness l (val := val)
                have hr := eval_completeness r (val := val)
                have h2 := BoolStepStar.lessArStepStarLeft l (ArExpr.Const (eval V val l)) r hl
                have h3 := BoolStepStar.lessArStepStarRight (ArExpr.Const (eval V val l)) r (ArExpr.Const (eval V val r)) hr
                have h4 := chasles_step_star V h2 h3
                exact chasles_step_star V h4 (step_to_stepstar V h1)
                exact h