l3lab/ntp-mathlib huggingface dataset:
/-- Corecursor for `Seq α` as a coinductive type. Iterates `f` to produce new elements
  of the sequence until `none` is obtained. -/
def corec (f : β → Option (α × β)) (b : β) : Seq α := by
  refine' ⟨Stream'.corec' (Corec.f f) (some b), fun {n} h => _⟩
  rw [Stream'.corec'_eq]
  change Stream'.corec' (Corec.f f) (Corec.f f (some b)).2 n = none
  revert h; generalize some b = o; revert o
  induction' n with n IH <;> intro o
  · change (Corec.f f o).1 = none → (Corec.f f (Corec.f f o).2).1 = none
    cases' o with b <;> intro h
    · rfl
    dsimp [Corec.f] at h
    dsimp [Corec.f]
    revert h; cases' h₁: f b with s <;> intro h
    · 

Proof state:
case zero.some.none
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b✝ b : β
h₁ : f b = none
h :
  (match none with
      | none => (none, none)
      | some (a, b') => (some a, some b')).1 =
    none
⊢ (match
        (match none with
          | none => (none, none)
          | some (a, b') => (some a, some b')).2 with
      | none => (none, none)
      | some b =>
        match f b with
        | none => (none, none)
        | some (a, b') => (some a, some b')).1 =
    none

Next tactic:
rfl
/-- Corecursor for `Seq α` as a coinductive type. Iterates `f` to produce new elements
  of the sequence until `none` is obtained. -/
def corec (f : β → Option (α × β)) (b : β) : Seq α := by
  refine' ⟨Stream'.corec' (Corec.f f) (some b), fun {n} h => _⟩
  rw [Stream'.corec'_eq]
  change Stream'.corec' (Corec.f f) (Corec.f f (some b)).2 n = none
  revert h; generalize some b = o; revert o
  induction' n with n IH <;> intro o
  · change (Corec.f f o).1 = none → (Corec.f f (Corec.f f o).2).1 = none
    cases' o with b <;> intro h
    · rfl
    dsimp [Corec.f] at h
    dsimp [Corec.f]
    revert h; cases' h₁: f b with s <;> intro h
    · rfl
    · 

Proof state:
case zero.some.some
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b✝ b : β
s : α × β
h₁ : f b = some s
h :
  (match some s with
      | none => (none, none)
      | some (a, b') => (some a, some b')).1 =
    none
⊢ (match
        (match some s with
          | none => (none, none)
          | some (a, b') => (some a, some b')).2 with
      | none => (none, none)
      | some b =>
        match f b with
        | none => (none, none)
        | some (a, b') => (some a, some b')).1 =
    none

Next tactic:
cases' s with a b'
/-- Corecursor for `Seq α` as a coinductive type. Iterates `f` to produce new elements
  of the sequence until `none` is obtained. -/
def corec (f : β → Option (α × β)) (b : β) : Seq α := by
  refine' ⟨Stream'.corec' (Corec.f f) (some b), fun {n} h => _⟩
  rw [Stream'.corec'_eq]
  change Stream'.corec' (Corec.f f) (Corec.f f (some b)).2 n = none
  revert h; generalize some b = o; revert o
  induction' n with n IH <;> intro o
  · change (Corec.f f o).1 = none → (Corec.f f (Corec.f f o).2).1 = none
    cases' o with b <;> intro h
    · rfl
    dsimp [Corec.f] at h
    dsimp [Corec.f]
    revert h; cases' h₁: f b with s <;> intro h
    · rfl
    · cases' s with a b'
      

Proof state:
case zero.some.some.mk
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b✝ b : β
a : α
b' : β
h₁ : f b = some (a, b')
h :
  (match some (a, b') with
      | none => (none, none)
      | some (a, b') => (some a, some b')).1 =
    none
⊢ (match
        (match some (a, b') with
          | none => (none, none)
          | some (a, b') => (some a, some b')).2 with
      | none => (none, none)
      | some b =>
        match f b with
        | none => (none, none)
        | some (a, b') => (some a, some b')).1 =
    none

Next tactic:
contradiction
/-- Corecursor for `Seq α` as a coinductive type. Iterates `f` to produce new elements
  of the sequence until `none` is obtained. -/
def corec (f : β → Option (α × β)) (b : β) : Seq α := by
  refine' ⟨Stream'.corec' (Corec.f f) (some b), fun {n} h => _⟩
  rw [Stream'.corec'_eq]
  change Stream'.corec' (Corec.f f) (Corec.f f (some b)).2 n = none
  revert h; generalize some b = o; revert o
  induction' n with n IH <;> intro o
  · change (Corec.f f o).1 = none → (Corec.f f (Corec.f f o).2).1 = none
    cases' o with b <;> intro h
    · rfl
    dsimp [Corec.f] at h
    dsimp [Corec.f]
    revert h; cases' h₁: f b with s <;> intro h
    · rfl
    · cases' s with a b'
      contradiction
  · 

Proof state:
case succ
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
n : ℕ
IH : ∀ (o : Option β), corec' (Corec.f f) o n = none → corec' (Corec.f f) (Corec.f f o).2 n = none
o : Option β
⊢ corec' (Corec.f f) o (Nat.succ n) = none → corec' (Corec.f f) (Corec.f f o).2 (Nat.succ n) = none

Next tactic:
rw [Stream'.corec'_eq (Corec.f f) (Corec.f f o).2, Stream'.corec'_eq (Corec.f f) o]
/-- Corecursor for `Seq α` as a coinductive type. Iterates `f` to produce new elements
  of the sequence until `none` is obtained. -/
def corec (f : β → Option (α × β)) (b : β) : Seq α := by
  refine' ⟨Stream'.corec' (Corec.f f) (some b), fun {n} h => _⟩
  rw [Stream'.corec'_eq]
  change Stream'.corec' (Corec.f f) (Corec.f f (some b)).2 n = none
  revert h; generalize some b = o; revert o
  induction' n with n IH <;> intro o
  · change (Corec.f f o).1 = none → (Corec.f f (Corec.f f o).2).1 = none
    cases' o with b <;> intro h
    · rfl
    dsimp [Corec.f] at h
    dsimp [Corec.f]
    revert h; cases' h₁: f b with s <;> intro h
    · rfl
    · cases' s with a b'
      contradiction
  · 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 : β → Option (α × β)
b : β
n : ℕ
IH : ∀ (o : Option β), corec' (Corec.f f) o n = none → corec' (Corec.f f) (Corec.f f o).2 n = none
o : Option β
⊢ ((Corec.f f o).1 :: corec' (Corec.f f) (Corec.f f o).2) (Nat.succ n) = none →
    ((Corec.f f (Corec.f f o).2).1 :: corec' (Corec.f f) (Corec.f f (Corec.f f o).2).2) (Nat.succ n) = none

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

Proof state:
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
⊢ destruct (corec f b) = omap (corec f) (f b)

Next tactic:
dsimp [corec, destruct, get]
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  

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

Next tactic:
have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  

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

Next tactic:
rw [h]
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  

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

Next tactic:
dsimp [Corec.f]
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  

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

Next tactic:
induction' h : f b with s
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  induction' h : f b with s; · 

Proof state:
case none
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
h✝ : corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1
h : f b = none
⊢ Option.map
      (fun a' =>
        (a',
          tail
            { val := corec' (Corec.f f) (some b),
              property :=
                (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b) n = none → corec' (Corec.f f) (some b) (n + 1) = none) }))
      (match none with
        | none => (none, none)
        | some (a, b') => (some a, some b')).1 =
    match none with
    | none => none
    | some (a, b) =>
      some
        (a,
          { val := corec' (Corec.f f) (some b),
            property :=
              (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b) n = none → corec' (Corec.f f) (some b) (n + 1) = none) })

Next tactic:
rfl
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  induction' h : f b with s; · rfl
  

Proof state:
case some
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
h✝ : corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1
s : α × β
h : f b = some s
⊢ Option.map
      (fun a' =>
        (a',
          tail
            { val := corec' (Corec.f f) (some b),
              property :=
                (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b) n = none → corec' (Corec.f f) (some b) (n + 1) = none) }))
      (match some s with
        | none => (none, none)
        | some (a, b') => (some a, some b')).1 =
    match some s with
    | none => none
    | some (a, b) =>
      some
        (a,
          { val := corec' (Corec.f f) (some b),
            property :=
              (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b) n = none → corec' (Corec.f f) (some b) (n + 1) = none) })

Next tactic:
cases' s with a b'
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  induction' h : f b with s; · rfl
  cases' s with a b'; 

Proof state:
case some.mk
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
h✝ : corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1
a : α
b' : β
h : f b = some (a, b')
⊢ Option.map
      (fun a' =>
        (a',
          tail
            { val := corec' (Corec.f f) (some b),
              property :=
                (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b) n = none → corec' (Corec.f f) (some b) (n + 1) = none) }))
      (match some (a, b') with
        | none => (none, none)
        | some (a, b') => (some a, some b')).1 =
    match some (a, b') with
    | none => none
    | some (a, b) =>
      some
        (a,
          { val := corec' (Corec.f f) (some b),
            property :=
              (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b) n = none → corec' (Corec.f f) (some b) (n + 1) = none) })

Next tactic:
dsimp [Corec.f]
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  induction' h : f b with s; · rfl
  cases' s with a b'; dsimp [Corec.f]
  

Proof state:
case some.mk
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
h✝ : corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1
a : α
b' : β
h : f b = some (a, b')
⊢ some
      (a,
        tail
          { val := corec' (Corec.f f) (some b),
            property :=
              (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b) n = none → corec' (Corec.f f) (some b) (n + 1) = none) }) =
    some
      (a,
        { val := corec' (Corec.f f) (some b'),
          property :=
            (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b') n = none → corec' (Corec.f f) (some b') (n + 1) = none) })

Next tactic:
apply congr_arg fun b' => some (a, b')
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  induction' h : f b with s; · rfl
  cases' s with a b'; dsimp [Corec.f]
  apply congr_arg fun b' => some (a, b')
  

Proof state:
case some.mk
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
h✝ : corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1
a : α
b' : β
h : f b = some (a, b')
⊢ tail
      { val := corec' (Corec.f f) (some b),
        property :=
          (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b) n = none → corec' (Corec.f f) (some b) (n + 1) = none) } =
    { val := corec' (Corec.f f) (some b'),
      property := (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b') n = none → corec' (Corec.f f) (some b') (n + 1) = none) }

Next tactic:
apply Subtype.eq
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  induction' h : f b with s; · rfl
  cases' s with a b'; dsimp [Corec.f]
  apply congr_arg fun b' => some (a, b')
  apply Subtype.eq
  

Proof state:
case some.mk.a
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
h✝ : corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1
a : α
b' : β
h : f b = some (a, b')
⊢ ↑(tail
        { val := corec' (Corec.f f) (some b),
          property :=
            (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b) n = none → corec' (Corec.f f) (some b) (n + 1) = none) }) =
    ↑{ val := corec' (Corec.f f) (some b'),
        property :=
          (_ : ∀ {n : ℕ}, corec' (Corec.f f) (some b') n = none → corec' (Corec.f f) (some b') (n + 1) = none) }

Next tactic:
dsimp [corec, tail]
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  induction' h : f b with s; · rfl
  cases' s with a b'; dsimp [Corec.f]
  apply congr_arg fun b' => some (a, b')
  apply Subtype.eq
  dsimp [corec, tail]
  

Proof state:
case some.mk.a
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
h✝ : corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1
a : α
b' : β
h : f b = some (a, b')
⊢ Stream'.tail (corec' (Corec.f f) (some b)) = corec' (Corec.f f) (some b')

Next tactic:
rw [Stream'.corec'_eq, Stream'.tail_cons]
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  induction' h : f b with s; · rfl
  cases' s with a b'; dsimp [Corec.f]
  apply congr_arg fun b' => some (a, b')
  apply Subtype.eq
  dsimp [corec, tail]
  rw [Stream'.corec'_eq, Stream'.tail_cons]
  

Proof state:
case some.mk.a
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
h✝ : corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1
a : α
b' : β
h : f b = some (a, b')
⊢ corec' (Corec.f f) (Corec.f f (some b)).2 = corec' (Corec.f f) (some b')

Next tactic:
dsimp [Corec.f]
@[simp]
theorem corec_eq (f : β → Option (α × β)) (b : β) :
    destruct (corec f b) = omap (corec f) (f b) := by
  dsimp [corec, destruct, get]
  -- porting note: next two lines were `change`...`with`...
  have h: Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 := rfl
  rw [h]
  dsimp [Corec.f]
  induction' h : f b with s; · rfl
  cases' s with a b'; dsimp [Corec.f]
  apply congr_arg fun b' => some (a, b')
  apply Subtype.eq
  dsimp [corec, tail]
  rw [Stream'.corec'_eq, Stream'.tail_cons]
  dsimp [Corec.f]; 

Proof state:
case some.mk.a
α : Type u
β : Type v
γ : Type w
f : β → Option (α × β)
b : β
h✝ : corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1
a : α
b' : β
h : f b = some (a, b')
⊢ corec' (Corec.f f)
      (match f b with
        | none => (none, none)
        | some (a, b') => (some a, some b')).2 =
    corec' (Corec.f f) (some 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 : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
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 : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
r : R s₁ s₂
⊢ ↑s₁ = ↑s₂

Next tactic:
apply Stream'.eq_of_bisim fun x y => ∃ s s' : Seq α, 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' : Seq α, s.1 = x ∧ s'.1 = y ∧ R s s'
  

Proof state:
case a.bisim
α : Type u
β : Type v
γ : Type w
R : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
r : R s₁ s₂
⊢ Stream'.IsBisimulation fun x y => ∃ s s', ↑s = x ∧ ↑s' = y ∧ R s s'
case a.a
α : Type u
β : Type v
γ : Type w
R : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
r : R s₁ s₂
⊢ ∃ s s', ↑s = ↑s₁ ∧ ↑s' = ↑s₂ ∧ 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' : Seq α, s.1 = x ∧ s'.1 = y ∧ R s s'
  dsimp [Stream'.IsBisimulation]
  

Proof state:
case a.bisim
α : Type u
β : Type v
γ : Type w
R : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
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'
case a.a
α : Type u
β : Type v
γ : Type w
R : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
r : R s₁ s₂
⊢ ∃ s s', ↑s = ↑s₁ ∧ ↑s' = ↑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' : Seq α, 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 : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
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'
case a.a
α : Type u
β : Type v
γ : Type w
R : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
r : R s₁ s₂
⊢ ∃ s s', ↑s = ↑s₁ ∧ ↑s' = ↑s₂ ∧ R s s'

Next tactic:
exact
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ => by
      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 := bisim r; revert r this
      apply recOn s _ _ <;> apply recOn s' _ _
      · intro r _
        constructor
        · rfl
        · assumption
      · intro x s _ this
        rw [destruct_nil, destruct_cons] at this
        exact False.elim this
      · intro x s _ this
        rw [destruct_nil, destruct_cons] at this
        exact False.elim this
      · intro x s x' s' _ this
        rw [destruct_cons, destruct_cons] at this
        rw [head_cons, head_cons, tail_cons, tail_cons]
        cases' this with h1 h2
        constructor
        rw [h1]
        exact h2
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' : Seq α, s.1 = x ∧ s'.1 = y ∧ R s s'
  dsimp [Stream'.IsBisimulation]
  intro t₁ t₂ e
  exact
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ => by
      

Proof state:
α : Type u
β : Type v
γ : Type w
R : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
r✝ : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
s s' : Seq α
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' : Seq α, s.1 = x ∧ s'.1 = y ∧ R s s'
  dsimp [Stream'.IsBisimulation]
  intro t₁ t₂ e
  exact
    match t₁, t₂, e with
    | _, _, ⟨s, s', rfl, rfl, r⟩ => by
      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 : Seq α → Seq α → Prop
bisim : IsBisimulation R
s₁ s₂ : Seq α
r✝¹ : R s₁ s₂
t₁ t₂ : Stream' (Option α)
e : ∃ s s', ↑s = t₁ ∧ ↑s' = t₂ ∧ R s s'
s s' : Seq α
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