-- Blase/Blase/AutoStructs/ForMathlib.lean

import Mathlib.Computability.NFA

import Mathlib.Data.FinEnum

import Mathlib.Data.Rel

import Mathlib.Data.Vector.Basic

import Blase.AutoStructs.ForLean

open Set

open Mathlib

open SetRel

namespace NFA

end NFA

def Std.HashMap.toPFun [BEq α] [Hashable α] (m : HashMap α β) (x : α) : Option β := m[x]?

-- 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: def Std.HashMap.toPFun [BEq α] [Hashable α] (m : HashMap α β) (x : α) : Option β := m[x]?

-- Local lemmas used:
content: theorem Std.HashMap.toPFun_toList[BEq α] [LawfulBEq α] [Hashable α] (m : HashMap α β) :
    m.toPFun = λ k ↦ m.toList.find? (λ x ↦ x.1 == k) |>.map Prod.snd

-- Target theorem and proof:
theorem Std.HashMap.fold_induction [BEq α] [LawfulBEq α] [DecidableEq α] [Hashable α]
  {f : γ → α → β → γ} {m : HashMap α β} {motive : γ → (α → Option β) → Prop} :
    motive b (λ _ ↦ none) →
    (∀ b x y m, m x = none → motive b m → motive (f b x y) (Function.update m x y)) →
    motive (m.fold f b) m.toPFun
:= by
  rintro hemp hind
  rw [Std.HashMap.fold_eq_foldl_toList, toPFun_toList]
  have := m.distinct_keys_toList
  revert this
  induction m.toList using List.reverseRecOn
  case nil =>
    simp_all
  case append_singleton xs xy ih =>
    rcases xy with ⟨x, y⟩
    rintro hd
    simp_all
    simp [List.pairwise_append] at hd
    rcases hd with ⟨hd, hnew⟩
    let f := fun k => Option.map Prod.snd (List.find? (fun x => x.1 == k) xs)
    specialize ih (by simp [hd])
    have hnewf : f x = none := by
      simp [f]; grind
    specialize hind _ x y _ hnewf ih
    convert hind using 1
    ext a b; simp
    rw [Function.update_apply]
    split_ifs with hcond
    · subst hcond; constructor
      · rintro ⟨a', hf | hf⟩
        · obtain h := List.find?_some hf
          simp at h; subst h
          grind [List.mem_of_find?_eq_some]
        · grind
      · simp only [Option.some.injEq, true_and]
        rintro rfl
        aesop
    · simp [f]; grind