-- LeroyCompilerVerificationCourse/Fixpoints.lean

import LeroyCompilerVerificationCourse.Imp

import LeroyCompilerVerificationCourse.Constprop

import Batteries.Data.List.Perm

open OrderStruct

open Monotone

section FixpointExistence

variable (α : Sort u) (F : α → α) [OrderWithBot α]

open OrderStruct OrderWithBot

end FixpointExistence

section Iterate

variable (α : Sort u) [inst : OrderStruct α] (F : α → α) [Monotone α F]

open OrderStruct

end Iterate

section Fixpoint

open OrderWithBot

variable {α : Sort u} [i : OrderWithBot α] (F : α → α) [Monotone α F]

end Fixpoint

section Constprop

open Std.HashMap

@[grind] def Eq' (S1 S2 : Store) : Prop := Equal S1 S2

@[grind] def Gt (S1 S2 : Store) := Le S2 S1 ∧ ¬ Eq' S2 S1

-- Repository definitions used:
content: def Equal (S1 S2 : Store) := Std.HashMap.Equiv S1 S2

content: def Store := Std.HashMap ident Int

content: @[grind] def Le (S1 S2 : Store) : Prop :=
  ∀ x n, S2.get? x = .some n -> S1.get? x = .some n

-- 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: @[grind] def Eq' (S1 S2 : Store) : Prop := Equal S1 S2

content: @[grind] def Gt (S1 S2 : Store) := Le S2 S1 ∧ ¬ Eq' S2 S1

-- Local lemmas used:
content: theorem hash_set_incl_size_leq (S1 S2 : Store) : Le S2 S1 → List.Subperm (S1.toList) (S2.toList)

content: @[grind] theorem Le_cardinal :
  ∀ S T : Store,
  Le T S ->
  S.size <= T.size ∧ (S.size = T.size → Equal S T)

content: @[grind] theorem Gt_cardinal :
  ∀ S S', Gt S S' -> S.size < S'.size

-- Target theorem and proof:
theorem Gt_wf : WellFounded Gt
:= by
  have := @InvImage Store Nat Nat.lt fun x => x.size
  have : ∀ (x y : Store), Gt x y → @InvImage Store Nat Nat.lt (fun x => x.size) x y := by
    intro x y heq
    unfold InvImage
    simp
    apply Gt_cardinal
    exact heq
  have subrel : Subrelation Gt (InvImage Nat.lt (fun x : Store => x.size)) := by
    intro x y gt; grind
  apply @Subrelation.wf Store (InvImage Nat.lt (fun x : Store => x.size)) Gt subrel
  exact InvImage.wf (fun x : Store => x.size) (Nat.lt_wfRel.wf)