File: Mathlib/Data/PFunctor/Univariate/M.lean

full_name: PFunctor.Approx.CofixA.intro
code: /-- `CofixA F n` is an `n` level approximation of an M-type -/
inductive CofixA : ℕ → Type u
  | continue : CofixA 0
  | intro {n} : ∀ a, (F.B a → CofixA n) → CofixA (succ n)
goal: (F.B a → PFunctor.Approx.CofixA F n) → PFunctor.Approx.CofixA F n.succ

full_name: PFunctor.Approx.CofixA
code: /-- `CofixA F n` is an `n` level approximation of an M-type -/
inductive CofixA : ℕ → Type u
  | continue : CofixA 0
  | intro {n} : ∀ a, (F.B a → CofixA n) → CofixA (succ n)
goal: ℕ → Type u

full_name: PFunctor.Approx.CofixA.continue
code: /-- `CofixA F n` is an `n` level approximation of an M-type -/
inductive CofixA : ℕ → Type u
  | continue : CofixA 0
  | intro {n} : ∀ a, (F.B a → CofixA n) → CofixA (succ n)
goal: PFunctor.Approx.CofixA F 0

full_name: PFunctor.Approx.CofixA.default
code: /-- default inhabitant of `CofixA` -/
protected def CofixA.default [Inhabited F.A] : ∀ n, CofixA F n
  
goal: PFunctor.Approx.CofixA F n

full_name: PFunctor.Approx.instInhabitedCofixAOfA
code: instance [Inhabited F.A] {n} : Inhabited (CofixA F n) 
goal: Inhabited (PFunctor.Approx.CofixA F n)

full_name: PFunctor.Approx.cofixA_eq_zero
code: theorem cofixA_eq_zero : ∀ x y : CofixA F 0, x = y
  
goal: x = y

full_name: PFunctor.Approx.head'
code: /-- The label of the root of the tree for a non-trivial
approximation of the cofix of a pfunctor.
-/
def head' : ∀ {n}, CofixA F (succ n) → F.A
  
goal: PFunctor.Approx.CofixA F n.succ → F.A

full_name: PFunctor.Approx.children'
code: /-- for a non-trivial approximation, return all the subtrees of the root -/
def children' : ∀ {n} (x : CofixA F (succ n)), F.B (head' x) → CofixA F n
  
goal: F.B (PFunctor.Approx.head' x) → PFunctor.Approx.CofixA F n

full_name: PFunctor.Approx.approx_eta
code: theorem approx_eta {n : ℕ} (x : CofixA F (n + 1)) : x = CofixA.intro (head' x) (children' x) 
goal: x = PFunctor.Approx.CofixA.intro (PFunctor.Approx.head' x) (PFunctor.Approx.children' x)

full_name: PFunctor.Approx.Agree
code: /-- Relation between two approximations of the cofix of a pfunctor
that state they both contain the same data until one of them is truncated -/
inductive Agree : ∀ {n : ℕ}, CofixA F n → CofixA F (n + 1) → Prop
  | continu (x : CofixA F 0) (y : CofixA F 1) : Agree x y
  | intro {n} {a} (x : F.B a → CofixA F n) (x' : F.B a → CofixA F (n + 1)) :
    (∀ i : F.B a, Agree (x i) (x' i)) → Agree (CofixA.intro a x) (CofixA.intro a x')
goal: PFunctor.Approx.CofixA F n → PFunctor.Approx.CofixA F (n + 1) → Prop

full_name: PFunctor.Approx.Agree.continu
code: /-- Relation between two approximations of the cofix of a pfunctor
that state they both contain the same data until one of them is truncated -/
inductive Agree : ∀ {n : ℕ}, CofixA F n → CofixA F (n + 1) → Prop
  | continu (x : CofixA F 0) (y : CofixA F 1) : Agree x y
  | intro {n} {a} (x : F.B a → CofixA F n) (x' : F.B a → CofixA F (n + 1)) :
    (∀ i : F.B a, Agree (x i) (x' i)) → Agree (CofixA.intro a x) (CofixA.intro a x')
goal: PFunctor.Approx.Agree x y

full_name: PFunctor.Approx.Agree.intro
code: /-- Relation between two approximations of the cofix of a pfunctor
that state they both contain the same data until one of them is truncated -/
inductive Agree : ∀ {n : ℕ}, CofixA F n → CofixA F (n + 1) → Prop
  | continu (x : CofixA F 0) (y : CofixA F 1) : Agree x y
  | intro {n} {a} (x : F.B a → CofixA F n) (x' : F.B a → CofixA F (n + 1)) :
    (∀ i : F.B a, Agree (x i) (x' i)) → Agree (CofixA.intro a x) (CofixA.intro a x')
goal: (∀ (i : F.B a), PFunctor.Approx.Agree (x i) (x' i)) →
  PFunctor.Approx.Agree (PFunctor.Approx.CofixA.intro a x) (PFunctor.Approx.CofixA.intro a x')

full_name: PFunctor.Approx.AllAgree
code: /-- Given an infinite series of approximations `approx`,
`AllAgree approx` states that they are all consistent with each other.
-/
def AllAgree (x : ∀ n, CofixA F n) 
goal: Prop

full_name: PFunctor.Approx.agree_trival
code: @[simp]
theorem agree_trival {x : CofixA F 0} {y : CofixA F 1} : Agree x y 
goal: PFunctor.Approx.Agree x y

full_name: PFunctor.Approx.agree_children
code: theorem agree_children {n : ℕ} (x : CofixA F (succ n)) (y : CofixA F (succ n + 1)) {i j}
    (h₀ : HEq i j) (h₁ : Agree x y) : Agree (children' x i) (children' y j) 
goal: PFunctor.Approx.Agree (PFunctor.Approx.children' x i) (PFunctor.Approx.children' y j)

full_name: PFunctor.Approx.truncate
code: /-- `truncate a` turns `a` into a more limited approximation -/
def truncate : ∀ {n : ℕ}, CofixA F (n + 1) → CofixA F n
  
goal: PFunctor.Approx.CofixA F (n + 1) → PFunctor.Approx.CofixA F n

full_name: PFunctor.Approx.truncate_eq_of_agree
code: theorem truncate_eq_of_agree {n : ℕ} (x : CofixA F n) (y : CofixA F (succ n)) (h : Agree x y) :
    truncate y = x 
goal: PFunctor.Approx.truncate y = x

full_name: PFunctor.Approx.sCorec
code: /-- `sCorec f i n` creates an approximation of height `n`
of the final coalgebra of `f` -/
def sCorec : X → ∀ n, CofixA F n
  
goal: X → (n : ℕ) → PFunctor.Approx.CofixA F n

full_name: PFunctor.Approx.P_corec
code: theorem P_corec (i : X) (n : ℕ) : Agree (sCorec f i n) (sCorec f i (succ n)) 
goal: PFunctor.Approx.Agree (PFunctor.Approx.sCorec f i n) (PFunctor.Approx.sCorec f i n.succ)

full_name: PFunctor.Approx.Path
code: /-- `Path F` provides indices to access internal nodes in `Corec F` -/
def Path (F : PFunctor.{u}) 
goal: Type u

full_name: PFunctor.Approx.Path.inhabited
code: instance Path.inhabited : Inhabited (Path F) 
goal: Inhabited (PFunctor.Approx.Path F)

full_name: PFunctor.Approx.CofixA.instSubsingleton
code: instance CofixA.instSubsingleton : Subsingleton (CofixA F 0) 
goal: Subsingleton (PFunctor.Approx.CofixA F 0)

full_name: PFunctor.Approx.head_succ'
code: theorem head_succ' (n m : ℕ) (x : ∀ n, CofixA F n) (Hconsistent : AllAgree x) :
    head' (x (succ n)) = head' (x (succ m)) 
goal: PFunctor.Approx.head' (x n.succ) = PFunctor.Approx.head' (x m.succ)

full_name: PFunctor.MIntl
code: /-- Internal definition for `M`. It is needed to avoid name clashes
between `M.mk` and `M.cases_on` and the declarations generated for
the structure -/
structure MIntl where
  /-- An `n`-th level approximation, for each depth `n` -/
  approx : ∀ n, CofixA F n
  /-- Each approximation agrees with the next -/
  consistent : AllAgree approx
goal: Type u

full_name: PFunctor.MIntl.approx
code: /-- Internal definition for `M`. It is needed to avoid name clashes
between `M.mk` and `M.cases_on` and the declarations generated for
the structure -/
structure MIntl where
  /-- An `n`-th level approximation, for each depth `n` -/
  approx : ∀ n, CofixA F n
  /-- Each approximation agrees with the next -/
  consistent : AllAgree approx
goal: PFunctor.Approx.CofixA F n

full_name: PFunctor.MIntl.consistent
code: /-- Internal definition for `M`. It is needed to avoid name clashes
between `M.mk` and `M.cases_on` and the declarations generated for
the structure -/
structure MIntl where
  /-- An `n`-th level approximation, for each depth `n` -/
  approx : ∀ n, CofixA F n
  /-- Each approximation agrees with the next -/
  consistent : AllAgree approx
goal: PFunctor.Approx.AllAgree self.approx

full_name: PFunctor.M
code: /-- For polynomial functor `F`, `M F` is its final coalgebra -/
def M 
goal: Type u

full_name: PFunctor.M.default_consistent
code: theorem M.default_consistent [Inhabited F.A] : ∀ n, Agree (default : CofixA F n) default
  
goal: PFunctor.Approx.Agree default default

full_name: PFunctor.M.inhabited
code: instance M.inhabited [Inhabited F.A] : Inhabited (M F) 
goal: Inhabited F.M

full_name: PFunctor.MIntl.inhabited
code: instance MIntl.inhabited [Inhabited F.A] : Inhabited (MIntl F) 
goal: Inhabited F.MIntl

full_name: PFunctor.M.ext'
code: theorem ext' (x y : M F) (H : ∀ i : ℕ, x.approx i = y.approx i) : x = y 
goal: x = y

full_name: PFunctor.M.corec
code: /-- Corecursor for the M-type defined by `F`. -/
protected def corec (i : X) : M F 
goal: F.M

full_name: PFunctor.M.head
code: /-- given a tree generated by `F`, `head` gives us the first piece of data
it contains -/
def head (x : M F) 
goal: F.A

full_name: PFunctor.M.children
code: /-- return all the subtrees of the root of a tree `x : M F` -/
def children (x : M F) (i : F.B (head x)) : M F 
goal: F.M

full_name: PFunctor.M.ichildren
code: /-- select a subtree using an `i : F.Idx` or return an arbitrary tree if
`i` designates no subtree of `x` -/
def ichildren [Inhabited (M F)] [DecidableEq F.A] (i : F.Idx) (x : M F) : M F 
goal: F.M

full_name: PFunctor.M.head_succ
code: theorem head_succ (n m : ℕ) (x : M F) : head' (x.approx (succ n)) = head' (x.approx (succ m)) 
goal: PFunctor.Approx.head' (x.approx n.succ) = PFunctor.Approx.head' (x.approx m.succ)

full_name: PFunctor.M.head_eq_head'
code: theorem head_eq_head' : ∀ (x : M F) (n : ℕ), head x = head' (x.approx <| n + 1)
  
goal: x.head = PFunctor.Approx.head' (x.approx (n + 1))

full_name: PFunctor.M.head'_eq_head
code: theorem head'_eq_head : ∀ (x : M F) (n : ℕ), head' (x.approx <| n + 1) = head x
  
goal: PFunctor.Approx.head' (x.approx (n + 1)) = x.head

full_name: PFunctor.M.truncate_approx
code: theorem truncate_approx (x : M F) (n : ℕ) : truncate (x.approx <| n + 1) = x.approx n 
goal: PFunctor.Approx.truncate (x.approx (n + 1)) = x.approx n

full_name: PFunctor.M.dest
code: /-- unfold an M-type -/
def dest : M F → F (M F)
  
goal: F.M → ↑F F.M

full_name: PFunctor.M.Approx.sMk
code: /-- generates the approximations needed for `M.mk` -/
protected def sMk (x : F (M F)) : ∀ n, CofixA F n
  
goal: PFunctor.Approx.CofixA F n

full_name: PFunctor.M.Approx.P_mk
code: protected theorem P_mk (x : F (M F)) : AllAgree (Approx.sMk x)
  
goal: PFunctor.Approx.AllAgree (PFunctor.M.Approx.sMk x)

full_name: PFunctor.M.mk
code: /-- constructor for M-types -/
protected def mk (x : F (M F)) : M F 
goal: F.M

full_name: PFunctor.M.Agree'.step
code: /-- `Agree' n` relates two trees of type `M F` that
are the same up to depth `n` -/
inductive Agree' : ℕ → M F → M F → Prop
  | trivial (x y : M F) : Agree' 0 x y
  | step {n : ℕ} {a} (x y : F.B a → M F) {x' y'} :
      x' = M.mk ⟨a, x⟩ → y' = M.mk ⟨a, y⟩ → (∀ i, Agree' n (x i) (y i)) → Agree' (succ n) x' y'
goal: x' = PFunctor.M.mk ⟨a, x⟩ →
  y' = PFunctor.M.mk ⟨a, y⟩ → (∀ (i : F.B a), PFunctor.M.Agree' n (x i) (y i)) → PFunctor.M.Agree' n.succ x' y'

full_name: PFunctor.M.Agree'
code: /-- `Agree' n` relates two trees of type `M F` that
are the same up to depth `n` -/
inductive Agree' : ℕ → M F → M F → Prop
  | trivial (x y : M F) : Agree' 0 x y
  | step {n : ℕ} {a} (x y : F.B a → M F) {x' y'} :
      x' = M.mk ⟨a, x⟩ → y' = M.mk ⟨a, y⟩ → (∀ i, Agree' n (x i) (y i)) → Agree' (succ n) x' y'
goal: ℕ → F.M → F.M → Prop

full_name: PFunctor.M.Agree'.trivial
code: /-- `Agree' n` relates two trees of type `M F` that
are the same up to depth `n` -/
inductive Agree' : ℕ → M F → M F → Prop
  | trivial (x y : M F) : Agree' 0 x y
  | step {n : ℕ} {a} (x y : F.B a → M F) {x' y'} :
      x' = M.mk ⟨a, x⟩ → y' = M.mk ⟨a, y⟩ → (∀ i, Agree' n (x i) (y i)) → Agree' (succ n) x' y'
goal: PFunctor.M.Agree' 0 x y

full_name: PFunctor.M.dest_mk
code: @[simp]
theorem dest_mk (x : F (M F)) : dest (M.mk x) = x 
goal: (PFunctor.M.mk x).dest = x

full_name: PFunctor.M.mk_dest
code: @[simp]
theorem mk_dest (x : M F) : M.mk (dest x) = x 
goal: PFunctor.M.mk x.dest = x

full_name: PFunctor.M.mk_inj
code: theorem mk_inj {x y : F (M F)} (h : M.mk x = M.mk y) : x = y 
goal: x = y

full_name: PFunctor.M.cases
code: /-- destructor for M-types -/
protected def cases {r : M F → Sort w} (f : ∀ x : F (M F), r (M.mk x)) (x : M F) : r x 
goal: r x

full_name: PFunctor.M.casesOn
code: /-- destructor for M-types -/
protected def casesOn {r : M F → Sort w} (x : M F) (f : ∀ x : F (M F), r (M.mk x)) : r x 
goal: r x

full_name: PFunctor.M.casesOn'
code: /-- destructor for M-types, similar to `casesOn` but also
gives access directly to the root and subtrees on an M-type -/
protected def casesOn' {r : M F → Sort w} (x : M F) (f : ∀ a f, r (M.mk ⟨a, f⟩)) : r x 
goal: r x

full_name: PFunctor.M.approx_mk
code: theorem approx_mk (a : F.A) (f : F.B a → M F) (i : ℕ) :
    (M.mk ⟨a, f⟩).approx (succ i) = CofixA.intro a fun j => (f j).approx i 
goal: (PFunctor.M.mk ⟨a, f⟩).approx i.succ = PFunctor.Approx.CofixA.intro a fun j => (f j).approx i

full_name: PFunctor.M.agree'_refl
code: @[simp]
theorem agree'_refl {n : ℕ} (x : M F) : Agree' n x x 
goal: PFunctor.M.Agree' n x x

full_name: PFunctor.M.agree_iff_agree'
code: theorem agree_iff_agree' {n : ℕ} (x y : M F) :
    Agree (x.approx n) (y.approx <| n + 1) ↔ Agree' n x y 
goal: PFunctor.Approx.Agree (x.approx n) (y.approx (n + 1)) ↔ PFunctor.M.Agree' n x y

full_name: PFunctor.M.cases_mk
code: @[simp]
theorem cases_mk {r : M F → Sort*} (x : F (M F)) (f : ∀ x : F (M F), r (M.mk x)) :
    PFunctor.M.cases f (M.mk x) = f x 
goal: PFunctor.M.cases f (PFunctor.M.mk x) = f x

full_name: PFunctor.M.casesOn_mk
code: @[simp]
theorem casesOn_mk {r : M F → Sort*} (x : F (M F)) (f : ∀ x : F (M F), r (M.mk x)) :
    PFunctor.M.casesOn (M.mk x) f = f x 
goal: (PFunctor.M.mk x).casesOn f = f x

full_name: PFunctor.M.casesOn_mk'
code: @[simp]
theorem casesOn_mk' {r : M F → Sort*} {a} (x : F.B a → M F)
    (f : ∀ (a) (f : F.B a → M F), r (M.mk ⟨a, f⟩)) :
    PFunctor.M.casesOn' (M.mk ⟨a, x⟩) f = f a x 
goal: (PFunctor.M.mk ⟨a, x⟩).casesOn' f = f a x

full_name: PFunctor.M.IsPath.cons
code: /-- `IsPath p x` tells us if `p` is a valid path through `x` -/
inductive IsPath : Path F → M F → Prop
  | nil (x : M F) : IsPath [] x
  |
  cons (xs : Path F) {a} (x : M F) (f : F.B a → M F) (i : F.B a) :
    x = M.mk ⟨a, f⟩ → IsPath xs (f i) → IsPath (⟨a, i⟩ :: xs) x
goal: x = PFunctor.M.mk ⟨a, f⟩ → PFunctor.M.IsPath xs (f i) → PFunctor.M.IsPath (⟨a, i⟩ :: xs) x

full_name: PFunctor.M.IsPath
code: /-- `IsPath p x` tells us if `p` is a valid path through `x` -/
inductive IsPath : Path F → M F → Prop
  | nil (x : M F) : IsPath [] x
  |
  cons (xs : Path F) {a} (x : M F) (f : F.B a → M F) (i : F.B a) :
    x = M.mk ⟨a, f⟩ → IsPath xs (f i) → IsPath (⟨a, i⟩ :: xs) x
goal: PFunctor.Approx.Path F → F.M → Prop

full_name: PFunctor.M.IsPath.nil
code: /-- `IsPath p x` tells us if `p` is a valid path through `x` -/
inductive IsPath : Path F → M F → Prop
  | nil (x : M F) : IsPath [] x
  |
  cons (xs : Path F) {a} (x : M F) (f : F.B a → M F) (i : F.B a) :
    x = M.mk ⟨a, f⟩ → IsPath xs (f i) → IsPath (⟨a, i⟩ :: xs) x
goal: PFunctor.M.IsPath [] x

full_name: PFunctor.M.isPath_cons
code: theorem isPath_cons {xs : Path F} {a a'} {f : F.B a → M F} {i : F.B a'} :
    IsPath (⟨a', i⟩ :: xs) (M.mk ⟨a, f⟩) → a = a' 
goal: PFunctor.M.IsPath (⟨a', i⟩ :: xs) (PFunctor.M.mk ⟨a, f⟩) → a = a'

full_name: PFunctor.M.isPath_cons'
code: theorem isPath_cons' {xs : Path F} {a} {f : F.B a → M F} {i : F.B a} :
    IsPath (⟨a, i⟩ :: xs) (M.mk ⟨a, f⟩) → IsPath xs (f i) 
goal: PFunctor.M.IsPath (⟨a, i⟩ :: xs) (PFunctor.M.mk ⟨a, f⟩) → PFunctor.M.IsPath xs (f i)

full_name: PFunctor.M.isubtree
code: /-- follow a path through a value of `M F` and return the subtree
found at the end of the path if it is a valid path for that value and
return a default tree -/
def isubtree [DecidableEq F.A] [Inhabited (M F)] : Path F → M F → M F
  
goal: PFunctor.Approx.Path F → F.M → F.M

full_name: PFunctor.M.iselect
code: /-- similar to `isubtree` but returns the data at the end of the path instead
of the whole subtree -/
def iselect [DecidableEq F.A] [Inhabited (M F)] (ps : Path F) : M F → F.A 
goal: F.M → F.A

full_name: PFunctor.M.iselect_eq_default
code: theorem iselect_eq_default [DecidableEq F.A] [Inhabited (M F)] (ps : Path F) (x : M F)
    (h : ¬IsPath ps x) : iselect ps x = head default 
goal: PFunctor.M.iselect ps x = default.head

full_name: PFunctor.M.head_mk
code: @[simp]
theorem head_mk (x : F (M F)) : head (M.mk x) = x.1 
goal: (PFunctor.M.mk x).head = x.fst

full_name: PFunctor.M.children_mk
code: theorem children_mk {a} (x : F.B a → M F) (i : F.B (head (M.mk ⟨a, x⟩))) :
    children (M.mk ⟨a, x⟩) i = x (cast (by rw [head_mk]) i) 
goal: (PFunctor.M.mk ⟨a, x⟩).children i = x (cast ⋯ i)

full_name: PFunctor.M.ichildren_mk
code: @[simp]
theorem ichildren_mk [DecidableEq F.A] [Inhabited (M F)] (x : F (M F)) (i : F.Idx) :
    ichildren i (M.mk x) = x.iget i 
goal: PFunctor.M.ichildren i (PFunctor.M.mk x) = x.iget i

full_name: PFunctor.M.isubtree_cons
code: @[simp]
theorem isubtree_cons [DecidableEq F.A] [Inhabited (M F)] (ps : Path F) {a} (f : F.B a → M F)
    {i : F.B a} : isubtree (⟨_, i⟩ :: ps) (M.mk ⟨a, f⟩) = isubtree ps (f i) 
goal: PFunctor.M.isubtree (⟨a, i⟩ :: ps) (PFunctor.M.mk ⟨a, f⟩) = PFunctor.M.isubtree ps (f i)

full_name: PFunctor.M.iselect_nil
code: @[simp]
theorem iselect_nil [DecidableEq F.A] [Inhabited (M F)] {a} (f : F.B a → M F) :
    iselect nil (M.mk ⟨a, f⟩) = a 
goal: PFunctor.M.iselect [] (PFunctor.M.mk ⟨a, f⟩) = a

full_name: PFunctor.M.iselect_cons
code: @[simp]
theorem iselect_cons [DecidableEq F.A] [Inhabited (M F)] (ps : Path F) {a} (f : F.B a → M F) {i} :
    iselect (⟨a, i⟩ :: ps) (M.mk ⟨a, f⟩) = iselect ps (f i) 
goal: PFunctor.M.iselect (⟨a, i⟩ :: ps) (PFunctor.M.mk ⟨a, f⟩) = PFunctor.M.iselect ps (f i)

full_name: PFunctor.M.corec_def
code: theorem corec_def {X} (f : X → F X) (x₀ : X) : M.corec f x₀ = M.mk (F.map (M.corec f) (f x₀)) 
goal: PFunctor.M.corec f x₀ = PFunctor.M.mk (F.map (PFunctor.M.corec f) (f x₀))

full_name: PFunctor.M.ext_aux
code: theorem ext_aux [Inhabited (M F)] [DecidableEq F.A] {n : ℕ} (x y z : M F) (hx : Agree' n z x)
    (hy : Agree' n z y) (hrec : ∀ ps : Path F, n = ps.length → iselect ps x = iselect ps y) :
    x.approx (n + 1) = y.approx (n + 1) 
goal: x.approx (n + 1) = y.approx (n + 1)

full_name: PFunctor.M.ext
code: theorem ext [Inhabited (M F)] (x y : M F) (H : ∀ ps : Path F, iselect ps x = iselect ps y) :
    x = y 
goal: x = y

full_name: PFunctor.M.«_aux_Mathlib_Data_PFunctor_Univariate_M___macroRules_PFunctor_M_term_~__1»
code: local infixl:50 " ~ " => R
goal: Lean.Macro

full_name: PFunctor.M.IsBisimulation
code: /-- Bisimulation is the standard proof technique for equality between
infinite tree-like structures -/
structure IsBisimulation : Prop where
  /-- The head of the trees are equal -/
  head : ∀ {a a'} {f f'}, M.mk ⟨a, f⟩ ~ M.mk ⟨a', f'⟩ → a = a'
  /-- The tails are equal -/
  tail : ∀ {a} {f f' : F.B a → M F}, M.mk ⟨a, f⟩ ~ M.mk ⟨a, f'⟩ → ∀ i : F.B a, f i ~ f' i
goal: Prop

full_name: PFunctor.M.IsBisimulation.tail
code: /-- Bisimulation is the standard proof technique for equality between
infinite tree-like structures -/
structure IsBisimulation : Prop where
  /-- The head of the trees are equal -/
  head : ∀ {a a'} {f f'}, M.mk ⟨a, f⟩ ~ M.mk ⟨a', f'⟩ → a = a'
  /-- The tails are equal -/
  tail : ∀ {a} {f f' : F.B a → M F}, M.mk ⟨a, f⟩ ~ M.mk ⟨a, f'⟩ → ∀ i : F.B a, f i ~ f' i
goal: R (PFunctor.M.mk ⟨a, f⟩) (PFunctor.M.mk ⟨a, f'⟩) → ∀ (i : F.B a), R (f i) (f' i)

full_name: PFunctor.M.IsBisimulation.head
code: /-- Bisimulation is the standard proof technique for equality between
infinite tree-like structures -/
structure IsBisimulation : Prop where
  /-- The head of the trees are equal -/
  head : ∀ {a a'} {f f'}, M.mk ⟨a, f⟩ ~ M.mk ⟨a', f'⟩ → a = a'
  /-- The tails are equal -/
  tail : ∀ {a} {f f' : F.B a → M F}, M.mk ⟨a, f⟩ ~ M.mk ⟨a, f'⟩ → ∀ i : F.B a, f i ~ f' i
goal: R (PFunctor.M.mk ⟨a, f⟩) (PFunctor.M.mk ⟨a', f'⟩) → a = a'

full_name: PFunctor.M.nth_of_bisim
code: theorem nth_of_bisim [Inhabited (M F)] (bisim : IsBisimulation R) (s₁ s₂) (ps : Path F) :
    (R s₁ s₂) →
      IsPath ps s₁ ∨ IsPath ps s₂ →
        iselect ps s₁ = iselect ps s₂ ∧
          ∃ (a : _) (f f' : F.B a → M F),
            isubtree ps s₁ = M.mk ⟨a, f⟩ ∧
              isubtree ps s₂ = M.mk ⟨a, f'⟩ ∧ ∀ i : F.B a, f i ~ f' i 
goal: R s₁ s₂ →
  PFunctor.M.IsPath ps s₁ ∨ PFunctor.M.IsPath ps s₂ →
    PFunctor.M.iselect ps s₁ = PFunctor.M.iselect ps s₂ ∧
      ∃ a f f',
        PFunctor.M.isubtree ps s₁ = PFunctor.M.mk ⟨a, f⟩ ∧
          PFunctor.M.isubtree ps s₂ = PFunctor.M.mk ⟨a, f'⟩ ∧ ∀ (i : F.B a), R (f i) (f' i)

full_name: PFunctor.M.eq_of_bisim
code: theorem eq_of_bisim [Nonempty (M F)] (bisim : IsBisimulation R) : ∀ s₁ s₂, R s₁ s₂ → s₁ = s₂ 
goal: R s₁ s₂ → s₁ = s₂

full_name: PFunctor.M.corecOn
code: /-- corecursor for `M F` with swapped arguments -/
def corecOn {X : Type*} (x₀ : X) (f : X → F X) : M F 
goal: F.M

full_name: PFunctor.M.dest_corec
code: theorem dest_corec (g : α → P α) (x : α) : M.dest (M.corec g x) = P.map (M.corec g) (g x) 
goal: (PFunctor.M.corec g x).dest = P.map (PFunctor.M.corec g) (g x)

full_name: PFunctor.M.bisim
code: theorem bisim (R : M P → M P → Prop)
    (h : ∀ x y, R x y → ∃ a f f', M.dest x = ⟨a, f⟩ ∧ M.dest y = ⟨a, f'⟩ ∧ ∀ i, R (f i) (f' i)) :
    ∀ x y, R x y → x = y 
goal: R x y → x = y

full_name: PFunctor.M.bisim'
code: theorem bisim' {α : Type*} (Q : α → Prop) (u v : α → M P)
    (h : ∀ x, Q x → ∃ a f f',
          M.dest (u x) = ⟨a, f⟩
          ∧ M.dest (v x) = ⟨a, f'⟩
          ∧ ∀ i, ∃ x', Q x' ∧ f i = u x' ∧ f' i = v x'
      ) :
    ∀ x, Q x → u x = v x 
goal: Q x → u x = v x

full_name: PFunctor.M.bisim_equiv
code: theorem bisim_equiv (R : M P → M P → Prop)
    (h : ∀ x y, R x y → ∃ a f f', M.dest x = ⟨a, f⟩ ∧ M.dest y = ⟨a, f'⟩ ∧ ∀ i, R (f i) (f' i)) :
    ∀ x y, R x y → x = y 
goal: R x y → x = y

full_name: PFunctor.M.corec_unique
code: theorem corec_unique (g : α → P α) (f : α → M P) (hyp : ∀ x, M.dest (f x) = P.map f (g x)) :
    f = M.corec g 
goal: f = PFunctor.M.corec g

full_name: PFunctor.M.corec₁
code: /-- corecursor where the state of the computation can be sent downstream
in the form of a recursive call -/
def corec₁ {α : Type u} (F : ∀ X, (α → X) → α → P X) : α → M P 
goal: α → P.M

full_name: PFunctor.M.corec'
code: /-- corecursor where it is possible to return a fully formed value at any point
of the computation -/
def corec' {α : Type u} (F : ∀ {X : Type u}, (α → X) → α → M P ⊕ P X) (x : α) : M P 
goal: P.M