l3lab/ntp-mathlib huggingface dataset:
theorem exists_of_mem_join {a : α} : ∀ {S : WSeq (WSeq α)}, a ∈ join S → ∃ s, s ∈ S ∧ a ∈ s := by
  suffices
    ∀ ss : WSeq α,
      a ∈ ss → ∀ s S, append s (join S) = ss → a ∈ append s (join S) → a ∈ s ∨ ∃ s, s ∈ S ∧ a ∈ s
    from fun S h => (this _ h nil S (by simp) (by simp [h])).resolve_left (not_mem_nil _)
  intro ss h; apply mem_rec_on h <;> [intro b ss o; intro ss IH] <;> intro s S
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;>
      simp at this; try cases this; try contradiction
    substs b' ss
    simp? at m ⊢ says simp only [cons_append, mem_cons_iff] at m ⊢
    cases' o with e IH
    · simp [e]
    cases' m with e m
    · simp [e]
    exact Or.imp_left Or.inr (IH _ _ rfl m)
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;> simp at this <;>
      try { try { have := this.1 }; contradiction } <;> subst ss
    · apply Or.inr
      -- Porting note: `exists_eq_or_imp` should be excluded.
      simp [-exists_eq_or_imp] at m ⊢
      cases' IH s S rfl m with as ex
      · exact ⟨s, Or.inl rfl, as⟩
      · rcases ex with ⟨s', sS, as⟩
        exact ⟨s', Or.inr sS, as⟩
    · apply Or.inr
      simp? at m says simp only [join_think, nil_append, mem_think] at m
      rcases (IH nil S (by 

Proof state:
α : Type u
β : Type v
γ : Type w
a : α
ss : WSeq α
h : a ∈ ss
S : WSeq (WSeq α)
IH :
  ∀ (s : WSeq α) (S_1 : WSeq (WSeq α)),
    append s (join S_1) = join S → a ∈ append s (join S_1) → a ∈ s ∨ ∃ s, s ∈ S_1 ∧ a ∈ s
ej : think (join S) = think (join S)
m : a ∈ join S
⊢ append nil (join S) = join S

Next tactic:
simp
theorem exists_of_mem_join {a : α} : ∀ {S : WSeq (WSeq α)}, a ∈ join S → ∃ s, s ∈ S ∧ a ∈ s := by
  suffices
    ∀ ss : WSeq α,
      a ∈ ss → ∀ s S, append s (join S) = ss → a ∈ append s (join S) → a ∈ s ∨ ∃ s, s ∈ S ∧ a ∈ s
    from fun S h => (this _ h nil S (by simp) (by simp [h])).resolve_left (not_mem_nil _)
  intro ss h; apply mem_rec_on h <;> [intro b ss o; intro ss IH] <;> intro s S
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;>
      simp at this; try cases this; try contradiction
    substs b' ss
    simp? at m ⊢ says simp only [cons_append, mem_cons_iff] at m ⊢
    cases' o with e IH
    · simp [e]
    cases' m with e m
    · simp [e]
    exact Or.imp_left Or.inr (IH _ _ rfl m)
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;> simp at this <;>
      try { try { have := this.1 }; contradiction } <;> subst ss
    · apply Or.inr
      -- Porting note: `exists_eq_or_imp` should be excluded.
      simp [-exists_eq_or_imp] at m ⊢
      cases' IH s S rfl m with as ex
      · exact ⟨s, Or.inl rfl, as⟩
      · rcases ex with ⟨s', sS, as⟩
        exact ⟨s', Or.inr sS, as⟩
    · apply Or.inr
      simp? at m says simp only [join_think, nil_append, mem_think] at m
      rcases (IH nil S (by simp) (by 

Proof state:
α : Type u
β : Type v
γ : Type w
a : α
ss : WSeq α
h : a ∈ ss
S : WSeq (WSeq α)
IH :
  ∀ (s : WSeq α) (S_1 : WSeq (WSeq α)),
    append s (join S_1) = join S → a ∈ append s (join S_1) → a ∈ s ∨ ∃ s, s ∈ S_1 ∧ a ∈ s
ej : think (join S) = think (join S)
m : a ∈ join S
⊢ a ∈ append nil (join S)

Next tactic:
simp [m]
theorem exists_of_mem_join {a : α} : ∀ {S : WSeq (WSeq α)}, a ∈ join S → ∃ s, s ∈ S ∧ a ∈ s := by
  suffices
    ∀ ss : WSeq α,
      a ∈ ss → ∀ s S, append s (join S) = ss → a ∈ append s (join S) → a ∈ s ∨ ∃ s, s ∈ S ∧ a ∈ s
    from fun S h => (this _ h nil S (by simp) (by simp [h])).resolve_left (not_mem_nil _)
  intro ss h; apply mem_rec_on h <;> [intro b ss o; intro ss IH] <;> intro s S
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;>
      simp at this; try cases this; try contradiction
    substs b' ss
    simp? at m ⊢ says simp only [cons_append, mem_cons_iff] at m ⊢
    cases' o with e IH
    · simp [e]
    cases' m with e m
    · simp [e]
    exact Or.imp_left Or.inr (IH _ _ rfl m)
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;> simp at this <;>
      try { try { have := this.1 }; contradiction } <;> subst ss
    · apply Or.inr
      -- Porting note: `exists_eq_or_imp` should be excluded.
      simp [-exists_eq_or_imp] at m ⊢
      cases' IH s S rfl m with as ex
      · exact ⟨s, Or.inl rfl, as⟩
      · rcases ex with ⟨s', sS, as⟩
        exact ⟨s', Or.inr sS, as⟩
    · apply Or.inr
      simp? at m says simp only [join_think, nil_append, mem_think] at m
      rcases (IH nil S (by simp) (by simp [m])).resolve_left (not_mem_nil _) with ⟨s, sS, as⟩
      

Proof state:
case h2.h1.h3.h.intro.intro
α : Type u
β : Type v
γ : Type w
a : α
ss : WSeq α
h : a ∈ ss
S : WSeq (WSeq α)
IH :
  ∀ (s : WSeq α) (S_1 : WSeq (WSeq α)),
    append s (join S_1) = join S → a ∈ append s (join S_1) → a ∈ s ∨ ∃ s, s ∈ S_1 ∧ a ∈ s
ej : think (join S) = think (join S)
m : a ∈ join S
s : WSeq α
sS : s ∈ S
as : a ∈ s
⊢ ∃ s, s ∈ think S ∧ a ∈ s

Next tactic:
exact ⟨s, by simp [sS], as⟩
theorem exists_of_mem_join {a : α} : ∀ {S : WSeq (WSeq α)}, a ∈ join S → ∃ s, s ∈ S ∧ a ∈ s := by
  suffices
    ∀ ss : WSeq α,
      a ∈ ss → ∀ s S, append s (join S) = ss → a ∈ append s (join S) → a ∈ s ∨ ∃ s, s ∈ S ∧ a ∈ s
    from fun S h => (this _ h nil S (by simp) (by simp [h])).resolve_left (not_mem_nil _)
  intro ss h; apply mem_rec_on h <;> [intro b ss o; intro ss IH] <;> intro s S
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;>
      simp at this; try cases this; try contradiction
    substs b' ss
    simp? at m ⊢ says simp only [cons_append, mem_cons_iff] at m ⊢
    cases' o with e IH
    · simp [e]
    cases' m with e m
    · simp [e]
    exact Or.imp_left Or.inr (IH _ _ rfl m)
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;> simp at this <;>
      try { try { have := this.1 }; contradiction } <;> subst ss
    · apply Or.inr
      -- Porting note: `exists_eq_or_imp` should be excluded.
      simp [-exists_eq_or_imp] at m ⊢
      cases' IH s S rfl m with as ex
      · exact ⟨s, Or.inl rfl, as⟩
      · rcases ex with ⟨s', sS, as⟩
        exact ⟨s', Or.inr sS, as⟩
    · apply Or.inr
      simp? at m says simp only [join_think, nil_append, mem_think] at m
      rcases (IH nil S (by simp) (by simp [m])).resolve_left (not_mem_nil _) with ⟨s, sS, as⟩
      exact ⟨s, by 

Proof state:
α : Type u
β : Type v
γ : Type w
a : α
ss : WSeq α
h : a ∈ ss
S : WSeq (WSeq α)
IH :
  ∀ (s : WSeq α) (S_1 : WSeq (WSeq α)),
    append s (join S_1) = join S → a ∈ append s (join S_1) → a ∈ s ∨ ∃ s, s ∈ S_1 ∧ a ∈ s
ej : think (join S) = think (join S)
m : a ∈ join S
s : WSeq α
sS : s ∈ S
as : a ∈ s
⊢ s ∈ think S

Next tactic:
simp [sS]
theorem exists_of_mem_join {a : α} : ∀ {S : WSeq (WSeq α)}, a ∈ join S → ∃ s, s ∈ S ∧ a ∈ s := by
  suffices
    ∀ ss : WSeq α,
      a ∈ ss → ∀ s S, append s (join S) = ss → a ∈ append s (join S) → a ∈ s ∨ ∃ s, s ∈ S ∧ a ∈ s
    from fun S h => (this _ h nil S (by simp) (by simp [h])).resolve_left (not_mem_nil _)
  intro ss h; apply mem_rec_on h <;> [intro b ss o; intro ss IH] <;> intro s S
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;>
      simp at this; try cases this; try contradiction
    substs b' ss
    simp? at m ⊢ says simp only [cons_append, mem_cons_iff] at m ⊢
    cases' o with e IH
    · simp [e]
    cases' m with e m
    · simp [e]
    exact Or.imp_left Or.inr (IH _ _ rfl m)
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;> simp at this <;>
      try { try { have := this.1 }; contradiction } <;> subst ss
    · apply Or.inr
      -- Porting note: `exists_eq_or_imp` should be excluded.
      simp [-exists_eq_or_imp] at m ⊢
      cases' IH s S rfl m with as ex
      · exact ⟨s, Or.inl rfl, as⟩
      · rcases ex with ⟨s', sS, as⟩
        exact ⟨s', Or.inr sS, as⟩
    · apply Or.inr
      simp? at m says simp only [join_think, nil_append, mem_think] at m
      rcases (IH nil S (by simp) (by simp [m])).resolve_left (not_mem_nil _) with ⟨s, sS, as⟩
      exact ⟨s, by simp [sS], as⟩
    · 

Proof state:
case h2.h3
α : Type u
β : Type v
γ : Type w
a : α
ss : WSeq α
h : a ∈ ss
S : WSeq (WSeq α)
s : WSeq α
m : a ∈ append (think s) (join S)
IH :
  ∀ (s_1 : WSeq α) (S_1 : WSeq (WSeq α)),
    append s_1 (join S_1) = append s (join S) → a ∈ append s_1 (join S_1) → a ∈ s_1 ∨ ∃ s, s ∈ S_1 ∧ a ∈ s
ej : think (append s (join S)) = think (append s (join S))
⊢ a ∈ think s ∨ ∃ s, s ∈ S ∧ a ∈ s

Next tactic:
simp at m IH ⊢
theorem exists_of_mem_join {a : α} : ∀ {S : WSeq (WSeq α)}, a ∈ join S → ∃ s, s ∈ S ∧ a ∈ s := by
  suffices
    ∀ ss : WSeq α,
      a ∈ ss → ∀ s S, append s (join S) = ss → a ∈ append s (join S) → a ∈ s ∨ ∃ s, s ∈ S ∧ a ∈ s
    from fun S h => (this _ h nil S (by simp) (by simp [h])).resolve_left (not_mem_nil _)
  intro ss h; apply mem_rec_on h <;> [intro b ss o; intro ss IH] <;> intro s S
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;>
      simp at this; try cases this; try contradiction
    substs b' ss
    simp? at m ⊢ says simp only [cons_append, mem_cons_iff] at m ⊢
    cases' o with e IH
    · simp [e]
    cases' m with e m
    · simp [e]
    exact Or.imp_left Or.inr (IH _ _ rfl m)
  · induction' s using WSeq.recOn with b' s s <;>
      [induction' S using WSeq.recOn with s S S; skip; skip] <;>
      intro ej m <;> simp at ej <;> have := congr_arg Seq.destruct ej <;> simp at this <;>
      try { try { have := this.1 }; contradiction } <;> subst ss
    · apply Or.inr
      -- Porting note: `exists_eq_or_imp` should be excluded.
      simp [-exists_eq_or_imp] at m ⊢
      cases' IH s S rfl m with as ex
      · exact ⟨s, Or.inl rfl, as⟩
      · rcases ex with ⟨s', sS, as⟩
        exact ⟨s', Or.inr sS, as⟩
    · apply Or.inr
      simp? at m says simp only [join_think, nil_append, mem_think] at m
      rcases (IH nil S (by simp) (by simp [m])).resolve_left (not_mem_nil _) with ⟨s, sS, as⟩
      exact ⟨s, by simp [sS], as⟩
    · simp at m IH ⊢
      

Proof state:
case h2.h3
α : Type u
β : Type v
γ : Type w
a : α
ss : WSeq α
h : a ∈ ss
S : WSeq (WSeq α)
s : WSeq α
IH :
  ∀ (s_1 : WSeq α) (S_1 : WSeq (WSeq α)),
    append s_1 (join S_1) = append s (join S) → a ∈ append s_1 (join S_1) → a ∈ s_1 ∨ ∃ s, s ∈ S_1 ∧ a ∈ s
ej : think (append s (join S)) = think (append s (join S))
m : a ∈ append s (join S)
⊢ a ∈ s ∨ ∃ s, s ∈ S ∧ a ∈ s

Next tactic:
apply IH _ _ rfl m
theorem exists_of_mem_bind {s : WSeq α} {f : α → WSeq β} {b} (h : b ∈ bind s f) :
    ∃ a ∈ s, b ∈ f a :=
  let ⟨t, tm, bt⟩ := exists_of_mem_join h
  let ⟨a, as, e⟩ := exists_of_mem_map tm
  ⟨a, as, by 

Proof state:
α : Type u
β : Type v
γ : Type w
s : WSeq α
f : α → WSeq β
b : β
h : b ∈ bind s f
t : WSeq β
tm : t ∈ map f s
bt : b ∈ t
a : α
as : a ∈ s
e : f a = t
⊢ b ∈ f a

Next tactic:
rwa [e]
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  

Proof state:
α : Type u
β : Type v
γ : Type w
f : α → β
s : WSeq α
⊢ destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s)

Next tactic:
apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
  · 

Proof state:
case bisim
α : Type u
β : Type v
γ : Type w
f : α → β
s : WSeq α
⊢ Computation.IsBisimulation fun c1 c2 =>
    ∃ s, c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)

Next tactic:
intro c1 c2 h
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
  · intro c1 c2 h
    

Proof state:
case bisim
α : Type u
β : Type v
γ : Type w
f : α → β
s : WSeq α
c1 c2 : Computation (Option (β × WSeq β))
h : ∃ s, c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
⊢ Computation.BisimO
    (fun c1 c2 => ∃ s, c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s))
    (Computation.destruct c1) (Computation.destruct c2)

Next tactic:
cases' h with s h
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
  · intro c1 c2 h
    cases' h with s h
    

Proof state:
case bisim.intro
α : Type u
β : Type v
γ : Type w
f : α → β
s✝ : WSeq α
c1 c2 : Computation (Option (β × WSeq β))
s : WSeq α
h : c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
⊢ Computation.BisimO
    (fun c1 c2 => ∃ s, c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s))
    (Computation.destruct c1) (Computation.destruct c2)

Next tactic:
rw [h.left, h.right]
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
  · intro c1 c2 h
    cases' h with s h
    rw [h.left, h.right]
    

Proof state:
case bisim.intro
α : Type u
β : Type v
γ : Type w
f : α → β
s✝ : WSeq α
c1 c2 : Computation (Option (β × WSeq β))
s : WSeq α
h : c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
⊢ Computation.BisimO
    (fun c1 c2 => ∃ s, c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s))
    (Computation.destruct (destruct (map f s)))
    (Computation.destruct (Computation.map (Option.map (Prod.map f (map f))) (destruct s)))

Next tactic:
induction' s using WSeq.recOn with a s s
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
  · intro c1 c2 h
    cases' h with s h
    rw [h.left, h.right]
    induction' s using WSeq.recOn with a s s <;> 

Proof state:
case bisim.intro.h1
α : Type u
β : Type v
γ : Type w
f : α → β
s : WSeq α
c1 c2 : Computation (Option (β × WSeq β))
h : c1 = destruct (map f nil) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct nil)
⊢ Computation.BisimO
    (fun c1 c2 => ∃ s, c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s))
    (Computation.destruct (destruct (map f nil)))
    (Computation.destruct (Computation.map (Option.map (Prod.map f (map f))) (destruct nil)))

Next tactic:
simp
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
  · intro c1 c2 h
    cases' h with s h
    rw [h.left, h.right]
    induction' s using WSeq.recOn with a s s <;> 

Proof state:
case bisim.intro.h2
α : Type u
β : Type v
γ : Type w
f : α → β
s✝ : WSeq α
c1 c2 : Computation (Option (β × WSeq β))
a : α
s : WSeq α
h : c1 = destruct (map f (cons a s)) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct (cons a s))
⊢ Computation.BisimO
    (fun c1 c2 => ∃ s, c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s))
    (Computation.destruct (destruct (map f (cons a s))))
    (Computation.destruct (Computation.map (Option.map (Prod.map f (map f))) (destruct (cons a s))))

Next tactic:
simp
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
  · intro c1 c2 h
    cases' h with s h
    rw [h.left, h.right]
    induction' s using WSeq.recOn with a s s <;> 

Proof state:
case bisim.intro.h3
α : Type u
β : Type v
γ : Type w
f : α → β
s✝ : WSeq α
c1 c2 : Computation (Option (β × WSeq β))
s : WSeq α
h : c1 = destruct (map f (think s)) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct (think s))
⊢ Computation.BisimO
    (fun c1 c2 => ∃ s, c1 = destruct (map f s) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s))
    (Computation.destruct (destruct (map f (think s))))
    (Computation.destruct (Computation.map (Option.map (Prod.map f (map f))) (destruct (think s))))

Next tactic:
simp
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
  · intro c1 c2 h
    cases' h with s h
    rw [h.left, h.right]
    induction' s using WSeq.recOn with a s s <;> simp
    

Proof state:
case bisim.intro.h3
α : Type u
β : Type v
γ : Type w
f : α → β
s✝ : WSeq α
c1 c2 : Computation (Option (β × WSeq β))
s : WSeq α
h : c1 = destruct (map f (think s)) ∧ c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct (think s))
⊢ ∃ s_1,
    destruct (map f s) = destruct (map f s_1) ∧
      Computation.map (Option.map (Prod.map f (map f))) (destruct s) =
        Computation.map (Option.map (Prod.map f (map f))) (destruct s_1)

Next tactic:
exact ⟨s, rfl, rfl⟩
theorem destruct_map (f : α → β) (s : WSeq α) :
    destruct (map f s) = Computation.map (Option.map (Prod.map f (map f))) (destruct s) := by
  apply
    Computation.eq_of_bisim fun c1 c2 =>
      ∃ s,
        c1 = destruct (map f s) ∧
          c2 = Computation.map (Option.map (Prod.map f (map f))) (destruct s)
  · intro c1 c2 h
    cases' h with s h
    rw [h.left, h.right]
    induction' s using WSeq.recOn with a s s <;> simp
    exact ⟨s, rfl, rfl⟩
  · 

Proof state:
case r
α : Type u
β : Type v
γ : Type w
f : α → β
s : WSeq α
⊢ ∃ s_1,
    destruct (map f s) = destruct (map f s_1) ∧
      Computation.map (Option.map (Prod.map f (map f))) (destruct s) =
        Computation.map (Option.map (Prod.map f (map f))) (destruct s_1)

Next tactic:
exact ⟨s, rfl, rfl⟩
theorem liftRel_map {δ} (R : α → β → Prop) (S : γ → δ → Prop) {s1 : WSeq α} {s2 : WSeq β}
    {f1 : α → γ} {f2 : β → δ} (h1 : LiftRel R s1 s2) (h2 : ∀ {a b}, R a b → S (f1 a) (f2 b)) :
    LiftRel S (map f1 s1) (map f2 s2) :=
  ⟨fun s1 s2 => ∃ s t, s1 = map f1 s ∧ s2 = map f2 t ∧ LiftRel R s t, ⟨s1, s2, rfl, rfl, h1⟩,
    fun {s1 s2} h =>
    match s1, s2, h with
    | _, _, ⟨s, t, rfl, rfl, h⟩ => by
      

Proof state:
α : Type u
β : Type v
γ : Type w
δ : Type u_1
R : α → β → Prop
S : γ → δ → Prop
s1✝ : WSeq α
s2✝ : WSeq β
f1 : α → γ
f2 : β → δ
h1 : LiftRel R s1✝ s2✝
h2 : ∀ {a : α} {b : β}, R a b → S (f1 a) (f2 b)
s1 : WSeq γ
s2 : WSeq δ
h✝ : (fun s1 s2 => ∃ s t, s1 = map f1 s ∧ s2 = map f2 t ∧ LiftRel R s t) s1 s2
s : WSeq α
t : WSeq β
h : LiftRel R s t
⊢ Computation.LiftRel (LiftRelO S fun s1 s2 => ∃ s t, s1 = map f1 s ∧ s2 = map f2 t ∧ LiftRel R s t)
    (destruct (map f1 s)) (destruct (map f2 t))

Next tactic:
simp [destruct_map]
theorem liftRel_map {δ} (R : α → β → Prop) (S : γ → δ → Prop) {s1 : WSeq α} {s2 : WSeq β}
    {f1 : α → γ} {f2 : β → δ} (h1 : LiftRel R s1 s2) (h2 : ∀ {a b}, R a b → S (f1 a) (f2 b)) :
    LiftRel S (map f1 s1) (map f2 s2) :=
  ⟨fun s1 s2 => ∃ s t, s1 = map f1 s ∧ s2 = map f2 t ∧ LiftRel R s t, ⟨s1, s2, rfl, rfl, h1⟩,
    fun {s1 s2} h =>
    match s1, s2, h with
    | _, _, ⟨s, t, rfl, rfl, h⟩ => by
      simp [destruct_map]; 

Proof state:
α : Type u
β : Type v
γ : Type w
δ : Type u_1
R : α → β → Prop
S : γ → δ → Prop
s1✝ : WSeq α
s2✝ : WSeq β
f1 : α → γ
f2 : β → δ
h1 : LiftRel R s1✝ s2✝
h2 : ∀ {a : α} {b : β}, R a b → S (f1 a) (f2 b)
s1 : WSeq γ
s2 : WSeq δ
h✝ : (fun s1 s2 => ∃ s t, s1 = map f1 s ∧ s2 = map f2 t ∧ LiftRel R s t) s1 s2
s : WSeq α
t : WSeq β
h : LiftRel R s t
⊢ Computation.LiftRel (LiftRelO S fun s1 s2 => ∃ s, s1 = map f1 s ∧ ∃ x, s2 = map f2 x ∧ LiftRel R s x)
    (Computation.map (Option.map (Prod.map f1 (map f1))) (destruct s))
    (Computation.map (Option.map (Prod.map f2 (map f2))) (destruct t))

Next tactic:
apply Computation.liftRel_map _ _ (liftRel_destruct h)
theorem liftRel_map {δ} (R : α → β → Prop) (S : γ → δ → Prop) {s1 : WSeq α} {s2 : WSeq β}
    {f1 : α → γ} {f2 : β → δ} (h1 : LiftRel R s1 s2) (h2 : ∀ {a b}, R a b → S (f1 a) (f2 b)) :
    LiftRel S (map f1 s1) (map f2 s2) :=
  ⟨fun s1 s2 => ∃ s t, s1 = map f1 s ∧ s2 = map f2 t ∧ LiftRel R s t, ⟨s1, s2, rfl, rfl, h1⟩,
    fun {s1 s2} h =>
    match s1, s2, h with
    | _, _, ⟨s, t, rfl, rfl, h⟩ => by
      simp [destruct_map]; apply Computation.liftRel_map _ _ (liftRel_destruct h)
      

Proof state:
α : Type u
β : Type v
γ : Type w
δ : Type u_1
R : α → β → Prop
S : γ → δ → Prop
s1✝ : WSeq α
s2✝ : WSeq β
f1 : α → γ
f2 : β → δ
h1 : LiftRel R s1✝ s2✝
h2 : ∀ {a : α} {b : β}, R a b → S (f1 a) (f2 b)
s1 : WSeq γ
s2 : WSeq δ
h✝ : (fun s1 s2 => ∃ s t, s1 = map f1 s ∧ s2 = map f2 t ∧ LiftRel R s t) s1 s2
s : WSeq α
t : WSeq β
h : LiftRel R s t
⊢ ∀ {a : Option (α × WSeq α)} {b : Option (β × WSeq β)},
    LiftRelO R (LiftRel R) a b →
      LiftRelO S (fun s1 s2 => ∃ s, s1 = map f1 s ∧ ∃ x, s2 = map f2 x ∧ LiftRel R s x)
        (Option.map (Prod.map f1 (map f1)) a) (Option.map (Prod.map f2 (map f2)) b)

Next tactic:
intro o p h