-- Blase/Blase/AutoStructs/Constructions.lean

import Mathlib.Data.Fintype.Prod

import Blase.AutoStructs.Worklist

import Mathlib.Tactic.ApplyFun

open SetRel

section sink

variable {A : Type} [BEq A] [Hashable A] [DecidableEq A] [FinEnum A]

end sink

section generic_prod

variable {α} [BEq α] [Hashable α] [LawfulBEq α]

variable {β} [BEq β] [Hashable β] [LawfulBEq β]

variable {S₁ : Finset α} {S₂ : Finset β}

variable {γ} (f : S₁ → S₂ → γ) (hinj : Function.Injective2 f)

variable {m₁ : Std.HashSet α} (hm₁ : ∀ s₁ ∈ m₁, s₁ ∈ S₁)

variable {m₂ : Std.HashSet β} (hm₂ : ∀ s₂ ∈ m₂, s₂ ∈ S₂)

end generic_prod

section product

variable {A : Type} [BEq A] [LawfulBEq A] [Hashable A] [DecidableEq A] [FinEnum A]

end product

section determinization

variable {A : Type} [BEq A] [LawfulBEq A] [Hashable A] [DecidableEq A] [FinEnum A]

private def bv_to_set (bv : BitVec w) : Set State :=
  { s | bv.getLsbD s }

-- Repository definitions used:
content: abbrev State := Nat

content: def RawCNFA.states (m : RawCNFA A) : Finset State := Finset.range m.stateMax

content: instance RawCNFA.statesFinset (m : RawCNFA A) : Fintype m.states := (Finset.range m.stateMax).fintypeCoeSort

content: structure CNFA (n : Nat) where
  m : RawCNFA (BitVec n)
  wf : m.WF

content: def Std.HashSet.toSet [BEq α] [Hashable α] (m : HashSet α) : Set α := { x | x ∈ m }

-- Repository lemmas used:
content: theorem Std.HashSet.fold_induction [BEq α] [LawfulBEq α] [Hashable α]
  {f : β → α → β} {m : HashSet α} {motive : β → Set α → Prop} :
    motive b ∅ →
    (∀ b x s, x ∉ s → motive b s → motive (f b x) (s ∪ {x})) →
    motive (m.fold f b) m.toSet

content: theorem Std.HashSet.toSet_toList[BEq α] [LawfulBEq α] [Hashable α] (m : HashSet α) : m.toSet = { x | x ∈ m.toList }

-- Local definitions used:
content: private def bv_to_set (bv : BitVec w) : Set State :=
  { s | bv.getLsbD s }

-- Local lemmas used:
content: @[simp]
lemma bv_to_set_or {m : CNFA n} (x y : BitVec m.m.stateMax) :
    (s ∈ bv_to_set (x ||| y)) ↔ (s ∈ bv_to_set x ∨ s ∈ bv_to_set y)

content: @[simp]
lemma bv_to_set_shift (x s : Nat) :
    (s ∈ bv_to_set (1#w <<< x)) ↔ (s = x ∧ x < w)

-- Target theorem and proof:
@[simp]
lemma transBV_spec {m : CNFA n} {res} {s : m.m.states} :
    s' ∈ bv_to_set (m.m.transBV' res s a) ↔
      (s' ∈ bv_to_set res ∨ s' ∈ m.m.tr s a)
:= by
  let motive (bv : BitVec m.m.stateMax) (X : Set State) :=
    ∀ s' (hlt : s' < m.m.stateMax),
      s' ∈ bv_to_set bv ↔ (s' ∈ bv_to_set res ∨ s' ∈ X)
  suffices h : motive (m.m.transBV' res s a) (m.m.tr s a).toSet by
    by_cases hlt : s' < m.m.stateMax
    · simp_all [motive]
    · constructor
      · simp_all [bv_to_set]
      · rintro (h | h); simp_all [bv_to_set]
        apply m.wf.trans_tgt_lt at h; simp_all only [RawCNFA.states, not_lt, Finset.mem_range]
        suffices _ : m.m.stateMax < m.m.stateMax by simp_all
        exact Nat.lt_of_le_of_lt hlt h
  apply Std.HashSet.fold_induction
  · simp [motive]
  rintro bv s S hnin ih s' hlt
  simp only [bv_to_set_or, bv_to_set_shift, Set.union_singleton, Set.mem_insert_iff]
  constructor
  · rintro (hold | ⟨rfl, hin⟩)
    · apply ih _ hlt |>.mp at hold; tauto
    · tauto
  · rintro (hold | rfl | hS)
    · left; apply (ih _ hlt).mpr; tauto
    · tauto
    · left; apply (ih _ hlt).mpr; tauto