l3lab/ntp-mathlib huggingface dataset:
/-- `corec f b` is the corecursor for `Computation α` as a coinductive type.
  If `f b = inl a` then `corec f b = pure a`, and if `f b = inl b'` then
  `corec f b = think (corec f b')`. -/
def corec (f : β → Sum α β) (b : β) : Computation α := by
  refine' ⟨Stream'.corec' (Corec.f f) (Sum.inr b), fun n a' h => _⟩
  rw [Stream'.corec'_eq]
  change Stream'.corec' (Corec.f f) (Corec.f f (Sum.inr b)).2 n = some a'
  revert h; generalize Sum.inr b = o; revert o
  induction' n with n IH <;> intro o
  · change (Corec.f f o).1 = some a' → (Corec.f f (Corec.f f o).2).1 = some a'
    cases' o with _ b <;> intro h
    · exact h
    unfold Corec.f at *; split <;> simp_all
  · rw [Stream'.corec'_eq (Corec.f f) (Corec.f f o).2, Stream'.corec'_eq (Corec.f f) o]
    

Proof state:
case succ
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b : β
a' : α
n : ℕ
IH : ∀ (o : α ⊕ β), Stream'.corec' (Corec.f f) o n = some a' → Stream'.corec' (Corec.f f) (Corec.f f o).2 n = some a'
o : α ⊕ β
⊢ Stream'.cons (Corec.f f o).1 (Stream'.corec' (Corec.f f) (Corec.f f o).2) (Nat.succ n) = some a' →
    Stream'.cons (Corec.f f (Corec.f f o).2).1 (Stream'.corec' (Corec.f f) (Corec.f f (Corec.f f o).2).2) (Nat.succ n) =
      some a'

Next tactic:
exact IH (Corec.f f o).2
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  

Proof state:
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b : β
⊢ destruct (corec f b) = rmap (corec f) (f b)

Next tactic:
dsimp [corec, destruct]
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  

Proof state:
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b : β
⊢ (match Stream'.corec' (Corec.f f) (Sum.inr b) 0 with
    | none =>
      Sum.inr
        (tail
          { val := Stream'.corec' (Corec.f f) (Sum.inr b),
            property :=
              (_ :
                ∀ (n : ℕ) (a' : α),
                  Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                    Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') })
    | some a => Sum.inl a) =
    match f b with
    | Sum.inl a => Sum.inl a
    | Sum.inr b =>
      Sum.inr
        { val := Stream'.corec' (Corec.f f) (Sum.inr b),
          property :=
            (_ :
              ∀ (n : ℕ) (a' : α),
                Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                  Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') }

Next tactic:
rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    

Proof state:
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b : β
⊢ Stream'.corec' (Corec.f f) (Sum.inr b) 0 = Sum.rec some (fun x => none) (f b)

Next tactic:
dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    

Proof state:
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b : β
⊢ (match f b with
    | Sum.inl a => some a
    | Sum.inr val => none) =
    Sum.rec some (fun x => none) (f b)

Next tactic:
match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => 

Proof state:
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b : β
x : α
⊢ (match Sum.inl x with
    | Sum.inl a => some a
    | Sum.inr val => none) =
    Sum.rec some (fun x => none) (Sum.inl x)

Next tactic:
rfl
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => 

Proof state:
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b x : β
⊢ (match Sum.inr x with
    | Sum.inl a => some a
    | Sum.inr val => none) =
    Sum.rec some (fun x => none) (Sum.inr x)

Next tactic:
rfl
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
  

Proof state:
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b : β
⊢ (match Sum.rec some (fun x => none) (f b) with
    | none =>
      Sum.inr
        (tail
          { val := Stream'.corec' (Corec.f f) (Sum.inr b),
            property :=
              (_ :
                ∀ (n : ℕ) (a' : α),
                  Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                    Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') })
    | some a => Sum.inl a) =
    match f b with
    | Sum.inl a => Sum.inl a
    | Sum.inr b =>
      Sum.inr
        { val := Stream'.corec' (Corec.f f) (Sum.inr b),
          property :=
            (_ :
              ∀ (n : ℕ) (a' : α),
                Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                  Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') }

Next tactic:
induction' h : f b with a b'
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
  induction' h : f b with a b'; · 

Proof state:
case inl
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b : β
a : α
h : f b = Sum.inl a
⊢ (match Sum.rec some (fun x => none) (Sum.inl a) with
    | none =>
      Sum.inr
        (tail
          { val := Stream'.corec' (Corec.f f) (Sum.inr b),
            property :=
              (_ :
                ∀ (n : ℕ) (a' : α),
                  Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                    Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') })
    | some a => Sum.inl a) =
    match Sum.inl a with
    | Sum.inl a => Sum.inl a
    | Sum.inr b =>
      Sum.inr
        { val := Stream'.corec' (Corec.f f) (Sum.inr b),
          property :=
            (_ :
              ∀ (n : ℕ) (a' : α),
                Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                  Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') }

Next tactic:
rfl
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
  induction' h : f b with a b'; · rfl
  

Proof state:
case inr
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b b' : β
h : f b = Sum.inr b'
⊢ (match Sum.rec some (fun x => none) (Sum.inr b') with
    | none =>
      Sum.inr
        (tail
          { val := Stream'.corec' (Corec.f f) (Sum.inr b),
            property :=
              (_ :
                ∀ (n : ℕ) (a' : α),
                  Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                    Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') })
    | some a => Sum.inl a) =
    match Sum.inr b' with
    | Sum.inl a => Sum.inl a
    | Sum.inr b =>
      Sum.inr
        { val := Stream'.corec' (Corec.f f) (Sum.inr b),
          property :=
            (_ :
              ∀ (n : ℕ) (a' : α),
                Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                  Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') }

Next tactic:
dsimp [Corec.f, destruct]
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
  induction' h : f b with a b'; · rfl
  dsimp [Corec.f, destruct]
  

Proof state:
case inr
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b b' : β
h : f b = Sum.inr b'
⊢ Sum.inr
      (tail
        { val := Stream'.corec' (Corec.f f) (Sum.inr b),
          property :=
            (_ :
              ∀ (n : ℕ) (a' : α),
                Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                  Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') }) =
    Sum.inr
      { val := Stream'.corec' (Corec.f f) (Sum.inr b'),
        property :=
          (_ :
            ∀ (n : ℕ) (a' : α),
              Stream'.corec' (Corec.f f) (Sum.inr b') n = some a' →
                Stream'.corec' (Corec.f f) (Sum.inr b') (n + 1) = some a') }

Next tactic:
apply congr_arg
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
  induction' h : f b with a b'; · rfl
  dsimp [Corec.f, destruct]
  apply congr_arg; 

Proof state:
case inr.h
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b b' : β
h : f b = Sum.inr b'
⊢ tail
      { val := Stream'.corec' (Corec.f f) (Sum.inr b),
        property :=
          (_ :
            ∀ (n : ℕ) (a' : α),
              Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') } =
    { val := Stream'.corec' (Corec.f f) (Sum.inr b'),
      property :=
        (_ :
          ∀ (n : ℕ) (a' : α),
            Stream'.corec' (Corec.f f) (Sum.inr b') n = some a' →
              Stream'.corec' (Corec.f f) (Sum.inr b') (n + 1) = some a') }

Next tactic:
apply Subtype.eq
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
  induction' h : f b with a b'; · rfl
  dsimp [Corec.f, destruct]
  apply congr_arg; apply Subtype.eq
  

Proof state:
case inr.h.a
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b b' : β
h : f b = Sum.inr b'
⊢ ↑(tail
        { val := Stream'.corec' (Corec.f f) (Sum.inr b),
          property :=
            (_ :
              ∀ (n : ℕ) (a' : α),
                Stream'.corec' (Corec.f f) (Sum.inr b) n = some a' →
                  Stream'.corec' (Corec.f f) (Sum.inr b) (n + 1) = some a') }) =
    ↑{ val := Stream'.corec' (Corec.f f) (Sum.inr b'),
        property :=
          (_ :
            ∀ (n : ℕ) (a' : α),
              Stream'.corec' (Corec.f f) (Sum.inr b') n = some a' →
                Stream'.corec' (Corec.f f) (Sum.inr b') (n + 1) = some a') }

Next tactic:
dsimp [corec, tail]
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
  induction' h : f b with a b'; · rfl
  dsimp [Corec.f, destruct]
  apply congr_arg; apply Subtype.eq
  dsimp [corec, tail]
  

Proof state:
case inr.h.a
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b b' : β
h : f b = Sum.inr b'
⊢ Stream'.tail (Stream'.corec' (Corec.f f) (Sum.inr b)) = Stream'.corec' (Corec.f f) (Sum.inr b')

Next tactic:
rw [Stream'.corec'_eq, Stream'.tail_cons]
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
  induction' h : f b with a b'; · rfl
  dsimp [Corec.f, destruct]
  apply congr_arg; apply Subtype.eq
  dsimp [corec, tail]
  rw [Stream'.corec'_eq, Stream'.tail_cons]
  

Proof state:
case inr.h.a
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b b' : β
h : f b = Sum.inr b'
⊢ Stream'.corec' (Corec.f f) (Corec.f f (Sum.inr b)).2 = Stream'.corec' (Corec.f f) (Sum.inr b')

Next tactic:
dsimp [Corec.f]
@[simp]
theorem corec_eq (f : β → Sum α β) (b : β) : destruct (corec f b) = rmap (corec f) (f b) := by
  dsimp [corec, destruct]
  rw [show Stream'.corec' (Corec.f f) (Sum.inr b) 0 =
    Sum.rec Option.some (λ _ => none) (f b) by
    dsimp [Corec.f, Stream'.corec', Stream'.corec, Stream'.map, Stream'.get, Stream'.iterate]
    match (f b) with
    | Sum.inl x => rfl
    | Sum.inr x => rfl
    ]
  induction' h : f b with a b'; · rfl
  dsimp [Corec.f, destruct]
  apply congr_arg; apply Subtype.eq
  dsimp [corec, tail]
  rw [Stream'.corec'_eq, Stream'.tail_cons]
  dsimp [Corec.f]; 

Proof state:
case inr.h.a
α : Type u
β : Type v
γ : Type w
f : β → α ⊕ β
b b' : β
h : f b = Sum.inr b'
⊢ Stream'.corec' (Corec.f f) (f b) = Stream'.corec' (Corec.f f) (Sum.inr b')

Next tactic:
rw [h]
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  

Proof state:
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r : R s₁ s₂
⊢ s₁ = s₂

Next tactic:
apply Subtype.eq
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  

Proof state:
case a
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r : R s₁ s₂
⊢ ↑s₁ = ↑s₂

Next tactic:
apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · 

Proof state:
case a.bisim
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r : R s₁ s₂
⊢ Stream'.IsBisimulation fun x y => ∃ s s', ↑s = x ∧ ↑s' = y ∧ R s s'

Next tactic:
dsimp [Stream'.IsBisimulation]
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · dsimp [Stream'.IsBisimulation]
    

Proof state:
case a.bisim
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r : R s₁ s₂
⊢ ∀ ⦃s₁ s₂ : Stream' (Option α)⦄,
    (∃ s s', ↑s = s₁ ∧ ↑s' = s₂ ∧ R s s') →
      Stream'.head s₁ = Stream'.head s₂ ∧ ∃ s s', ↑s = Stream'.tail s₁ ∧ ↑s' = Stream'.tail s₂ ∧ R s s'

Next tactic:
intro t₁ t₂ e
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · dsimp [Stream'.IsBisimulation]
    intro t₁ t₂ e
    

Proof state:
case a.bisim
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
⊢ Stream'.head t₁ = Stream'.head t₂ ∧ ∃ s s', ↑s = Stream'.tail t₁ ∧ ↑s' = Stream'.tail t₂ ∧ R s s'

Next tactic:
match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ =>
      suffices head s = head s' ∧ R (tail s) (tail s') from
        And.imp id (fun r => ⟨tail s, tail s', by cases s; rfl, by cases s'; rfl, r⟩) this
      have h := bisim r; revert r h
      apply recOn s _ _ <;> intro r' <;> apply recOn s' _ _ <;> intro a' r h
      · constructor <;> dsimp at h
        · rw [h]
        · rw [h] at r
          rw [tail_pure, tail_pure,h]
          assumption
      · rw [destruct_pure, destruct_think] at h
        exact False.elim h
      · rw [destruct_pure, destruct_think] at h
        exact False.elim h
      · simp at h
        simp [*]
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · dsimp [Stream'.IsBisimulation]
    intro t₁ t₂ e
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ =>
      

Proof state:
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r✝ : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
s s' : Computation α
r : R s s'
⊢ Stream'.head ↑s = Stream'.head ↑s' ∧ ∃ s_1 s'_1, ↑s_1 = Stream'.tail ↑s ∧ ↑s'_1 = Stream'.tail ↑s' ∧ R s_1 s'_1

Next tactic:
suffices head s = head s' ∧ R (tail s) (tail s') from
        And.imp id (fun r => ⟨tail s, tail s', by cases s; rfl, by cases s'; rfl, r⟩) this
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · dsimp [Stream'.IsBisimulation]
    intro t₁ t₂ e
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ =>
      suffices head s = head s' ∧ R (tail s) (tail s') from
        And.imp id (fun r => ⟨tail s, tail s', by 

Proof state:
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r✝¹ : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
s s' : Computation α
r✝ : R s s'
this : head s = head s' ∧ R (tail s) (tail s')
r : R (tail s) (tail s')
⊢ ↑(tail s) = Stream'.tail ↑s

Next tactic:
cases s
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · dsimp [Stream'.IsBisimulation]
    intro t₁ t₂ e
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ =>
      suffices head s = head s' ∧ R (tail s) (tail s') from
        And.imp id (fun r => ⟨tail s, tail s', by cases s; 

Proof state:
case mk
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r✝¹ : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
s' : Computation α
val✝ : Stream' (Option α)
property✝ : ∀ ⦃n : ℕ⦄ ⦃a : α⦄, val✝ n = some a → val✝ (n + 1) = some a
r✝ : R { val := val✝, property := property✝ } s'
this : head { val := val✝, property := property✝ } = head s' ∧ R (tail { val := val✝, property := property✝ }) (tail s')
r : R (tail { val := val✝, property := property✝ }) (tail s')
⊢ ↑(tail { val := val✝, property := property✝ }) = Stream'.tail ↑{ val := val✝, property := property✝ }

Next tactic:
rfl
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · dsimp [Stream'.IsBisimulation]
    intro t₁ t₂ e
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ =>
      suffices head s = head s' ∧ R (tail s) (tail s') from
        And.imp id (fun r => ⟨tail s, tail s', by cases s; rfl, by 

Proof state:
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r✝¹ : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
s s' : Computation α
r✝ : R s s'
this : head s = head s' ∧ R (tail s) (tail s')
r : R (tail s) (tail s')
⊢ ↑(tail s') = Stream'.tail ↑s'

Next tactic:
cases s'
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · dsimp [Stream'.IsBisimulation]
    intro t₁ t₂ e
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ =>
      suffices head s = head s' ∧ R (tail s) (tail s') from
        And.imp id (fun r => ⟨tail s, tail s', by cases s; rfl, by cases s'; 

Proof state:
case mk
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r✝¹ : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
s : Computation α
val✝ : Stream' (Option α)
property✝ : ∀ ⦃n : ℕ⦄ ⦃a : α⦄, val✝ n = some a → val✝ (n + 1) = some a
r✝ : R s { val := val✝, property := property✝ }
this : head s = head { val := val✝, property := property✝ } ∧ R (tail s) (tail { val := val✝, property := property✝ })
r : R (tail s) (tail { val := val✝, property := property✝ })
⊢ ↑(tail { val := val✝, property := property✝ }) = Stream'.tail ↑{ val := val✝, property := property✝ }

Next tactic:
rfl
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · dsimp [Stream'.IsBisimulation]
    intro t₁ t₂ e
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ =>
      suffices head s = head s' ∧ R (tail s) (tail s') from
        And.imp id (fun r => ⟨tail s, tail s', by cases s; rfl, by cases s'; rfl, r⟩) this
      

Proof state:
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r✝ : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
s s' : Computation α
r : R s s'
⊢ head s = head s' ∧ R (tail s) (tail s')

Next tactic:
have h := bisim r
theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by
  apply Subtype.eq
  apply Stream'.eq_of_bisim fun x y => ∃ s s' : Computation α, s.1 = x ∧ s'.1 = y ∧ R s s'
  · dsimp [Stream'.IsBisimulation]
    intro t₁ t₂ e
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ =>
      suffices head s = head s' ∧ R (tail s) (tail s') from
        And.imp id (fun r => ⟨tail s, tail s', by cases s; rfl, by cases s'; rfl, r⟩) this
      have h := bisim r; 

Proof state:
α : Type u
β : Type v
γ : Type w
R : Computation α → Computation α → Prop
bisim : IsBisimulation R
s₁ s₂ : Computation α
r✝ : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
s s' : Computation α
r : R s s'
h : BisimO R (destruct s) (destruct s')
⊢ head s = head s' ∧ R (tail s) (tail s')

Next tactic:
revert r h