-- Frap/SmallStep.lean

inductive Tm : Type :=
  | c : Nat → Tm       
  | p : Tm → Tm → Tm   

open Tm

open Eval

namespace SimpleArith1

inductive Step : Tm → Tm → Prop :=
  | st_plusConstConst n₁ n₂ : Step (p (c n₁) (c n₂)) (c (n₁ + n₂))
  | st_plus1 t₁ t₁' t₂ : Step t₁ t₁' → Step (p t₁ t₂) (p t₁' t₂)
  | st_plus2 n₁ t₂ t₂' : Step t₂ t₂' → Step (p (c n₁) t₂) (p (c n₁) t₂')

open Step

end SimpleArith1

def relation (X : Type) := X → X → Prop

namespace SimpleArith2

open SimpleArith1

end SimpleArith2

inductive Value : Tm → Prop :=
  | v_const n : Value (c n)

open Value

inductive Step : Tm → Tm → Prop :=
  | st_plusConstConst n₁ n₂ : Step (p (c n₁) (c n₂)) (c (n₁ + n₂))
  | st_plus1 t₁ t₁' t₂ : Step t₁ t₁' → Step (p t₁ t₂) (p t₁' t₂)
  | st_plus2 v₁ t₂ t₂' : Value v₁ → Step t₂ t₂' → Step (p v₁ t₂) (p v₁ t₂')

open Step

def normal_form {X : Type} (R : relation X) (t : X) : Prop := ¬∃t', R t t'

inductive Multi {X : Type} (R : relation X) : relation X :=
  | multi_refl x : Multi R x x
  | multi_step x y z : R x y → Multi R y z → Multi R x z

open Multi

def normalizing {X : Type} (R : relation X) :=
  ∀t, ∃t', (Multi R) t t' ∧ normal_form R t'

-- Repository definitions used:
content: ...

-- Repository lemmas used:
content: theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) :\n(l₁ ++ l₂)[i] = l₁[i]

-- Local definitions used:
content: inductive Tm : Type :=
  | c : Nat → Tm       
  | p : Tm → Tm → Tm

content: inductive Step : Tm → Tm → Prop :=
  | st_plusConstConst n₁ n₂ : Step (p (c n₁) (c n₂)) (c (n₁ + n₂))
  | st_plus1 t₁ t₁' t₂ : Step t₁ t₁' → Step (p t₁ t₂) (p t₁' t₂)
  | st_plus2 n₁ t₂ t₂' : Step t₂ t₂' → Step (p (c n₁) t₂) (p (c n₁) t₂')

content: def relation (X : Type) := X → X → Prop

content: inductive Value : Tm → Prop :=
  | v_const n : Value (c n)

content: inductive Step : Tm → Tm → Prop :=
  | st_plusConstConst n₁ n₂ : Step (p (c n₁) (c n₂)) (c (n₁ + n₂))
  | st_plus1 t₁ t₁' t₂ : Step t₁ t₁' → Step (p t₁ t₂) (p t₁' t₂)
  | st_plus2 v₁ t₂ t₂' : Value v₁ → Step t₂ t₂' → Step (p v₁ t₂) (p v₁ t₂')

content: def normal_form {X : Type} (R : relation X) (t : X) : Prop := ¬∃t', R t t'

content: inductive Multi {X : Type} (R : relation X) : relation X :=
  | multi_refl x : Multi R x x
  | multi_step x y z : R x y → Multi R y z → Multi R x z

content: def normalizing {X : Type} (R : relation X) :=
  ∀t, ∃t', (Multi R) t t' ∧ normal_form R t'

-- Local lemmas used:
content: theorem strong_progress t : Value t ∨ ∃t', Step t t'

content: theorem value_is_nf v : Value v → normal_form Step v

content: theorem nf_is_value t : normal_form Step t → Value t

content: theorem nf_same_as_value t : normal_form Step t ↔ Value t

content: theorem multi_R (X : Type) (R : relation X) x y : R x y → Multi R x y

content: theorem multi_trans (X : Type) (R : relation X) x y z
    : Multi R x y → Multi R y z → Multi R x z

content: theorem multistep_congr_1 t₁ t₁' t₂
    : (t₁ ~~>* t₁') → (p t₁ t₂ ~~>* p t₁' t₂)

content: theorem multistep_congr_2 v₁ t₂ t₂'
    : (t₂ ~~>* t₂') → (p v₁ t₂ ~~>* p v₁ t₂')

-- Target theorem and proof:
theorem step_normalizing : normalizing Step
:= by
  unfold normalizing
  intro t
  induction t with
  | c n =>
    exists c n
    constructor
    . apply multi_refl
    . rw [nf_same_as_value]; apply v_const
  | p t₁ t₂ ih₁ ih₂ =>
    obtain ⟨t₁', ⟨hs₁, hn₁⟩⟩ := ih₁
    obtain ⟨t₂', ⟨hs₂, hn₂⟩⟩ := ih₂
    rw [nf_same_as_value] at hn₁
    rw [nf_same_as_value] at hn₂
    cases hn₁; cases hn₂
    rename_i n₁ n₂
    exists c (n₁ + n₂)
    constructor
    . apply multi_trans
      . apply multistep_congr_1
        apply hs₁
      . apply multi_trans
        . apply multistep_congr_2
          apply hs₂
        . apply multi_R
          apply st_plusConstConst
    . rw [nf_same_as_value]; apply v_const