-- ToMathlib/Control/Monad/Equiv.lean

import ToMathlib.Control.Monad.Hom

import Mathlib.Logic.Function.Defs

structure NatEquiv (m : Type u → Type v) (n : Type u → Type w) where
  toFun : {α : Type u} → m α → n α
  invFun : {α : Type u} → n α → m α
  left_inv : ∀ {α}, Function.LeftInverse (@invFun α) (@toFun α) := by admit /- proof elided -/

namespace NatEquiv

variable {m : Type u → Type v} {n : Type u → Type w} {p : Type u → Type z}

end NatEquiv

namespace PureEquiv

variable {m : Type u → Type v} [Pure m] {n : Type u → Type w} [Pure n]

end PureEquiv

structure BindEquiv (m : Type u → Type v) [Bind m] (n : Type u → Type w) [Bind n]
    extends NatEquiv m n where
  map_bind' {α β : Type u} (x : m α) (y : α → m β) :
    toFun (x >>= y) = toFun x >>= (fun a => toFun (y a))

namespace BindEquiv

variable {m : Type u → Type v} [Bind m] {n : Type u → Type w} [Bind n]

-- Repository definitions used:
content: structure NatHom (m : Type u → Type v) (n : Type u → Type w) where
  toFun : {α : Type u} → m α → n α

-- Repository lemmas used:
content: theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) :\n(l₁ ++ l₂)[i] = l₁[i]

-- Local definitions used:
content: structure NatEquiv (m : Type u → Type v) (n : Type u → Type w) where
  toFun : {α : Type u} → m α → n α
  invFun : {α : Type u} → n α → m α
  left_inv : ∀ {α}, Function.LeftInverse (@invFun α) (@toFun α) := by admit /- proof elided -/

content: structure BindEquiv (m : Type u → Type v) [Bind m] (n : Type u → Type w) [Bind n]
    extends NatEquiv m n where
  map_bind' {α β : Type u} (x : m α) (y : α → m β) :
    toFun (x >>= y) = toFun x >>= (fun a => toFun (y a))

-- Local lemmas used:

-- Target theorem and proof:
@[simp]
lemma map_bind_inv (f : BindEquiv m n) {α β : Type u} (x : n α) (y : α → n β) :
    f.invFun (x >>= y) = f.invFun x >>= (fun a => f.invFun (y a))
:= by
  -- We'll show f.toFun applied to both sides gives the same result
  have h1 : f.toFun (f.invFun (x >>= y)) = x >>= y := f.right_inv (x >>= y)
  have h2 : f.toFun (f.invFun x >>= (fun a => f.invFun (y a))) =
            f.toFun (f.invFun x) >>= (fun a => f.toFun (f.invFun (y a))) := f.map_bind' _ _
  have h3 : f.toFun (f.invFun x) = x := f.right_inv x
  have h4 : ∀ a, f.toFun (f.invFun (y a)) = y a := fun a => f.right_inv (y a)
  have h5 : f.toFun (f.invFun x >>= (fun a => f.invFun (y a))) = x >>= y := by
    rw [h2, h3]
    congr 1
    ext a
    exact h4 a
  have h6 : f.toFun (f.invFun (x >>= y)) = f.toFun (f.invFun x >>= (fun a => f.invFun (y a))) := by
    rw [h1, h5]
  exact Function.LeftInverse.injective f.left_inv h6