/-
Copyright (c) 2022 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies

! This file was ported from Lean 3 source module data.option.n_ary
! leanprover-community/mathlib commit 995b47e555f1b6297c7cf16855f1023e355219fb
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Option.Basic

/-!
# Binary map of options

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file defines the binary map of `option`. This is mostly useful to define pointwise operations
on intervals.

## Main declarations

* `option.map₂`: Binary map of options.

## Notes

This file is very similar to `data.set.n_ary`, `data.finset.n_ary` and `order.filter.n_ary`. Please
keep them in sync.

We do not define `option.map₃` as its only purpose so far would be to prove properties of
`option.map₂` and casing already fulfills this task.
-/

open Function

namespace Option

variable {α α' β β' γ γ' δ δ' ε ε' : Type _} {f : α → β → γ} {a : Option α} {b : Option β}
  {c : Option γ}

#print Option.map₂ /-
/-- The image of a binary function `f : α → β → γ` as a function `option α → option β → option γ`.
Mathematically this should be thought of as the image of the corresponding function `α × β → γ`. -/
def map₂ (f : α → β → γ) (a : Option α) (b : Option β) : Option γ :=
  a.bind fun a => b.map <| f a
#align option.map₂ Option.map₂
-/

/- warning: option.map₂_def -> Option.map₂_def is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u1}} {γ : Type.{u1}} (f : α -> β -> γ) (a : Option.{u1} α) (b : Option.{u1} β), Eq.{succ u1} (Option.{u1} γ) (Option.map₂.{u1, u1, u1} α β γ f a b) (Seq.seq.{u1, u1} Option.{u1} (Applicative.toHasSeq.{u1, u1} Option.{u1} (Monad.toApplicative.{u1, u1} Option.{u1} Option.monad.{u1})) β γ (Functor.map.{u1, u1} Option.{u1} (Traversable.toFunctor.{u1} Option.{u1} Option.traversable.{u1}) α (β -> γ) f a) b)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u1}} {γ : Type.{u1}} (f : α -> β -> γ) (a : Option.{u1} α) (b : Option.{u1} β), Eq.{succ u1} (Option.{u1} γ) (Option.map₂.{u1, u1, u1} α β γ f a b) (Seq.seq.{u1, u1} Option.{u1} (Applicative.toSeq.{u1, u1} Option.{u1} (Alternative.toApplicative.{u1, u1} Option.{u1} instAlternativeOption.{u1})) β γ (Functor.map.{u1, u1} Option.{u1} instFunctorOption.{u1} α (β -> γ) f a) (fun (x._@.Mathlib.Data.Option.NAry._hyg.142 : Unit) => b))
Case conversion may be inaccurate. Consider using '#align option.map₂_def Option.map₂_defₓ'. -/
/-- `option.map₂` in terms of monadic operations. Note that this can't be taken as the definition
because of the lack of universe polymorphism. -/
theorem map₂_def {α β γ : Type _} (f : α → β → γ) (a : Option α) (b : Option β) :
    map₂ f a b = f <$> a <*> b := by cases a <;> rfl
#align option.map₂_def Option.map₂_def

/- warning: option.map₂_some_some -> Option.map₂_some_some is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> β -> γ) (a : α) (b : β), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ f (Option.some.{u1} α a) (Option.some.{u2} β b)) (Option.some.{u3} γ (f a b))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} (f : α -> β -> γ) (a : α) (b : β), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u2, u1, u3} α β γ f (Option.some.{u2} α a) (Option.some.{u1} β b)) (Option.some.{u3} γ (f a b))
Case conversion may be inaccurate. Consider using '#align option.map₂_some_some Option.map₂_some_someₓ'. -/
@[simp]
theorem map₂_some_some (f : α → β → γ) (a : α) (b : β) : map₂ f (some a) (some b) = some (f a b) :=
  rfl
#align option.map₂_some_some Option.map₂_some_some

/- warning: option.map₂_coe_coe -> Option.map₂_coe_coe is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> β -> γ) (a : α) (b : β), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ f ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (Option.{u1} α) (HasLiftT.mk.{succ u1, succ u1} α (Option.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} α (Option.{u1} α) (coeOption.{u1} α))) a) ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) β (Option.{u2} β) (HasLiftT.mk.{succ u2, succ u2} β (Option.{u2} β) (CoeTCₓ.coe.{succ u2, succ u2} β (Option.{u2} β) (coeOption.{u2} β))) b)) ((fun (a : Type.{u3}) (b : Type.{u3}) [self : HasLiftT.{succ u3, succ u3} a b] => self.0) γ (Option.{u3} γ) (HasLiftT.mk.{succ u3, succ u3} γ (Option.{u3} γ) (CoeTCₓ.coe.{succ u3, succ u3} γ (Option.{u3} γ) (coeOption.{u3} γ))) (f a b))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} (f : α -> β -> γ) (a : α) (b : β), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u2, u1, u3} α β γ f (Option.some.{u2} α a) (Option.some.{u1} β b)) (Option.some.{u3} γ (f a b))
Case conversion may be inaccurate. Consider using '#align option.map₂_coe_coe Option.map₂_coe_coeₓ'. -/
theorem map₂_coe_coe (f : α → β → γ) (a : α) (b : β) : map₂ f a b = f a b :=
  rfl
#align option.map₂_coe_coe Option.map₂_coe_coe

/- warning: option.map₂_none_left -> Option.map₂_none_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> β -> γ) (b : Option.{u2} β), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ f (Option.none.{u1} α) b) (Option.none.{u3} γ)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} (f : α -> β -> γ) (b : Option.{u3} β), Eq.{succ u2} (Option.{u2} γ) (Option.map₂.{u1, u3, u2} α β γ f (Option.none.{u1} α) b) (Option.none.{u2} γ)
Case conversion may be inaccurate. Consider using '#align option.map₂_none_left Option.map₂_none_leftₓ'. -/
@[simp]
theorem map₂_none_left (f : α → β → γ) (b : Option β) : map₂ f none b = none :=
  rfl
#align option.map₂_none_left Option.map₂_none_left

/- warning: option.map₂_none_right -> Option.map₂_none_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> β -> γ) (a : Option.{u1} α), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ f a (Option.none.{u2} β)) (Option.none.{u3} γ)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u1}} {γ : Type.{u2}} (f : α -> β -> γ) (a : Option.{u3} α), Eq.{succ u2} (Option.{u2} γ) (Option.map₂.{u3, u1, u2} α β γ f a (Option.none.{u1} β)) (Option.none.{u2} γ)
Case conversion may be inaccurate. Consider using '#align option.map₂_none_right Option.map₂_none_rightₓ'. -/
@[simp]
theorem map₂_none_right (f : α → β → γ) (a : Option α) : map₂ f a none = none := by cases a <;> rfl
#align option.map₂_none_right Option.map₂_none_right

/- warning: option.map₂_coe_left -> Option.map₂_coe_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> β -> γ) (a : α) (b : Option.{u2} β), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ f ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (Option.{u1} α) (HasLiftT.mk.{succ u1, succ u1} α (Option.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} α (Option.{u1} α) (coeOption.{u1} α))) a) b) (Option.map.{u2, u3} β γ (fun (b : β) => f a b) b)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} (f : α -> β -> γ) (a : α) (b : Option.{u3} β), Eq.{succ u2} (Option.{u2} γ) (Option.map₂.{u1, u3, u2} α β γ f (Option.some.{u1} α a) b) (Option.map.{u3, u2} β γ (fun (b : β) => f a b) b)
Case conversion may be inaccurate. Consider using '#align option.map₂_coe_left Option.map₂_coe_leftₓ'. -/
@[simp]
theorem map₂_coe_left (f : α → β → γ) (a : α) (b : Option β) : map₂ f a b = b.map fun b => f a b :=
  rfl
#align option.map₂_coe_left Option.map₂_coe_left

/- warning: option.map₂_coe_right -> Option.map₂_coe_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> β -> γ) (a : Option.{u1} α) (b : β), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ f a ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) β (Option.{u2} β) (HasLiftT.mk.{succ u2, succ u2} β (Option.{u2} β) (CoeTCₓ.coe.{succ u2, succ u2} β (Option.{u2} β) (coeOption.{u2} β))) b)) (Option.map.{u1, u3} α γ (fun (a : α) => f a b) a)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u1}} {γ : Type.{u2}} (f : α -> β -> γ) (a : Option.{u3} α) (b : β), Eq.{succ u2} (Option.{u2} γ) (Option.map₂.{u3, u1, u2} α β γ f a (Option.some.{u1} β b)) (Option.map.{u3, u2} α γ (fun (a : α) => f a b) a)
Case conversion may be inaccurate. Consider using '#align option.map₂_coe_right Option.map₂_coe_rightₓ'. -/
@[simp]
theorem map₂_coe_right (f : α → β → γ) (a : Option α) (b : β) : map₂ f a b = a.map fun a => f a b :=
  rfl
#align option.map₂_coe_right Option.map₂_coe_right

/- warning: option.mem_map₂_iff -> Option.mem_map₂_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {f : α -> β -> γ} {a : Option.{u1} α} {b : Option.{u2} β} {c : γ}, Iff (Membership.Mem.{u3, u3} γ (Option.{u3} γ) (Option.hasMem.{u3} γ) c (Option.map₂.{u1, u2, u3} α β γ f a b)) (Exists.{succ u1} α (fun (a' : α) => Exists.{succ u2} β (fun (b' : β) => And (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) a' a) (And (Membership.Mem.{u2, u2} β (Option.{u2} β) (Option.hasMem.{u2} β) b' b) (Eq.{succ u3} γ (f a' b') c)))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} {f : α -> β -> γ} {a : Option.{u2} α} {b : Option.{u1} β} {c : γ}, Iff (Membership.mem.{u3, u3} γ (Option.{u3} γ) (Option.instMembershipOption.{u3} γ) c (Option.map₂.{u2, u1, u3} α β γ f a b)) (Exists.{succ u2} α (fun (a' : α) => Exists.{succ u1} β (fun (b' : β) => And (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) a' a) (And (Membership.mem.{u1, u1} β (Option.{u1} β) (Option.instMembershipOption.{u1} β) b' b) (Eq.{succ u3} γ (f a' b') c)))))
Case conversion may be inaccurate. Consider using '#align option.mem_map₂_iff Option.mem_map₂_iffₓ'. -/
@[simp]
theorem mem_map₂_iff {c : γ} : c ∈ map₂ f a b ↔ ∃ a' b', a' ∈ a ∧ b' ∈ b ∧ f a' b' = c := by
  simp [map₂]
#align option.mem_map₂_iff Option.mem_map₂_iff

/- warning: option.map₂_eq_none_iff -> Option.map₂_eq_none_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {f : α -> β -> γ} {a : Option.{u1} α} {b : Option.{u2} β}, Iff (Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ f a b) (Option.none.{u3} γ)) (Or (Eq.{succ u1} (Option.{u1} α) a (Option.none.{u1} α)) (Eq.{succ u2} (Option.{u2} β) b (Option.none.{u2} β)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} {f : α -> β -> γ} {a : Option.{u2} α} {b : Option.{u1} β}, Iff (Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u2, u1, u3} α β γ f a b) (Option.none.{u3} γ)) (Or (Eq.{succ u2} (Option.{u2} α) a (Option.none.{u2} α)) (Eq.{succ u1} (Option.{u1} β) b (Option.none.{u1} β)))
Case conversion may be inaccurate. Consider using '#align option.map₂_eq_none_iff Option.map₂_eq_none_iffₓ'. -/
@[simp]
theorem map₂_eq_none_iff : map₂ f a b = none ↔ a = none ∨ b = none := by
  cases a <;> cases b <;> simp
#align option.map₂_eq_none_iff Option.map₂_eq_none_iff

/- warning: option.map₂_swap -> Option.map₂_swap is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> β -> γ) (a : Option.{u1} α) (b : Option.{u2} β), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ f a b) (Option.map₂.{u2, u1, u3} β α γ (fun (a : β) (b : α) => f b a) b a)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} (f : α -> β -> γ) (a : Option.{u3} α) (b : Option.{u2} β), Eq.{succ u1} (Option.{u1} γ) (Option.map₂.{u3, u2, u1} α β γ f a b) (Option.map₂.{u2, u3, u1} β α γ (fun (a : β) (b : α) => f b a) b a)
Case conversion may be inaccurate. Consider using '#align option.map₂_swap Option.map₂_swapₓ'. -/
theorem map₂_swap (f : α → β → γ) (a : Option α) (b : Option β) :
    map₂ f a b = map₂ (fun a b => f b a) b a := by cases a <;> cases b <;> rfl
#align option.map₂_swap Option.map₂_swap

/- warning: option.map_map₂ -> Option.map_map₂ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {a : Option.{u1} α} {b : Option.{u2} β} (f : α -> β -> γ) (g : γ -> δ), Eq.{succ u4} (Option.{u4} δ) (Option.map.{u3, u4} γ δ g (Option.map₂.{u1, u2, u3} α β γ f a b)) (Option.map₂.{u1, u2, u4} α β δ (fun (a : α) (b : β) => g (f a b)) a b)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} {δ : Option.{u2} α} {a : Option.{u1} β} {b : Type.{u4}} (f : α -> β -> γ) (g : γ -> b), Eq.{succ u4} (Option.{u4} b) (Option.map.{u3, u4} γ b g (Option.map₂.{u2, u1, u3} α β γ f δ a)) (Option.map₂.{u2, u1, u4} α β b (fun (a : α) (b : β) => g (f a b)) δ a)
Case conversion may be inaccurate. Consider using '#align option.map_map₂ Option.map_map₂ₓ'. -/
theorem map_map₂ (f : α → β → γ) (g : γ → δ) :
    (map₂ f a b).map g = map₂ (fun a b => g (f a b)) a b := by cases a <;> cases b <;> rfl
#align option.map_map₂ Option.map_map₂

/- warning: option.map₂_map_left -> Option.map₂_map_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {a : Option.{u1} α} {b : Option.{u2} β} (f : γ -> β -> δ) (g : α -> γ), Eq.{succ u4} (Option.{u4} δ) (Option.map₂.{u3, u2, u4} γ β δ f (Option.map.{u1, u3} α γ g a) b) (Option.map₂.{u1, u2, u4} α β δ (fun (a : α) (b : β) => f (g a) b) a b)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Option.{u1} α} {a : Option.{u2} β} {b : Type.{u4}} (f : γ -> β -> b) (g : α -> γ), Eq.{succ u4} (Option.{u4} b) (Option.map₂.{u3, u2, u4} γ β b f (Option.map.{u1, u3} α γ g δ) a) (Option.map₂.{u1, u2, u4} α β b (fun (a : α) (b : β) => f (g a) b) δ a)
Case conversion may be inaccurate. Consider using '#align option.map₂_map_left Option.map₂_map_leftₓ'. -/
theorem map₂_map_left (f : γ → β → δ) (g : α → γ) :
    map₂ f (a.map g) b = map₂ (fun a b => f (g a) b) a b := by cases a <;> rfl
#align option.map₂_map_left Option.map₂_map_left

/- warning: option.map₂_map_right -> Option.map₂_map_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {a : Option.{u1} α} {b : Option.{u2} β} (f : α -> γ -> δ) (g : β -> γ), Eq.{succ u4} (Option.{u4} δ) (Option.map₂.{u1, u3, u4} α γ δ f a (Option.map.{u2, u3} β γ g b)) (Option.map₂.{u1, u2, u4} α β δ (fun (a : α) (b : β) => f a (g b)) a b)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u1}} {γ : Type.{u2}} {δ : Option.{u3} α} {a : Option.{u1} β} {b : Type.{u4}} (f : α -> γ -> b) (g : β -> γ), Eq.{succ u4} (Option.{u4} b) (Option.map₂.{u3, u2, u4} α γ b f δ (Option.map.{u1, u2} β γ g a)) (Option.map₂.{u3, u1, u4} α β b (fun (a : α) (b : β) => f a (g b)) δ a)
Case conversion may be inaccurate. Consider using '#align option.map₂_map_right Option.map₂_map_rightₓ'. -/
theorem map₂_map_right (f : α → γ → δ) (g : β → γ) :
    map₂ f a (b.map g) = map₂ (fun a b => f a (g b)) a b := by cases b <;> rfl
#align option.map₂_map_right Option.map₂_map_right

/- warning: option.map₂_curry -> Option.map₂_curry is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : (Prod.{u1, u2} α β) -> γ) (a : Option.{u1} α) (b : Option.{u2} β), Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ (Function.curry.{u1, u2, u3} α β γ f) a b) (Option.map.{max u1 u2, u3} (Prod.{u1, u2} α β) γ f (Option.map₂.{u1, u2, max u1 u2} α β (Prod.{u1, u2} α β) (Prod.mk.{u1, u2} α β) a b))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} (f : (Prod.{u3, u2} α β) -> γ) (a : Option.{u3} α) (b : Option.{u2} β), Eq.{succ u1} (Option.{u1} γ) (Option.map₂.{u3, u2, u1} α β γ (Function.curry.{u3, u2, u1} α β γ f) a b) (Option.map.{max u3 u2, u1} (Prod.{u3, u2} α β) γ f (Option.map₂.{u3, u2, max u3 u2} α β (Prod.{u3, u2} α β) (Prod.mk.{u3, u2} α β) a b))
Case conversion may be inaccurate. Consider using '#align option.map₂_curry Option.map₂_curryₓ'. -/
@[simp]
theorem map₂_curry (f : α × β → γ) (a : Option α) (b : Option β) :
    map₂ (curry f) a b = Option.map f (map₂ Prod.mk a b) :=
  (map_map₂ _ _).symm
#align option.map₂_curry Option.map₂_curry

/- warning: option.map_uncurry -> Option.map_uncurry is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> β -> γ) (x : Option.{max u1 u2} (Prod.{u1, u2} α β)), Eq.{succ u3} (Option.{u3} γ) (Option.map.{max u1 u2, u3} (Prod.{u1, u2} α β) γ (Function.uncurry.{u1, u2, u3} α β γ f) x) (Option.map₂.{u1, u2, u3} α β γ f (Option.map.{max u1 u2, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) x) (Option.map.{max u1 u2, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) x))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u3}} {γ : Type.{u1}} (f : α -> β -> γ) (x : Option.{max u3 u2} (Prod.{u2, u3} α β)), Eq.{succ u1} (Option.{u1} γ) (Option.map.{max u3 u2, u1} (Prod.{u2, u3} α β) γ (Function.uncurry.{u2, u3, u1} α β γ f) x) (Option.map₂.{u2, u3, u1} α β γ f (Option.map.{max u3 u2, u2} (Prod.{u2, u3} α β) α (Prod.fst.{u2, u3} α β) x) (Option.map.{max u3 u2, u3} (Prod.{u2, u3} α β) β (Prod.snd.{u2, u3} α β) x))
Case conversion may be inaccurate. Consider using '#align option.map_uncurry Option.map_uncurryₓ'. -/
@[simp]
theorem map_uncurry (f : α → β → γ) (x : Option (α × β)) :
    x.map (uncurry f) = map₂ f (x.map Prod.fst) (x.map Prod.snd) := by cases x <;> rfl
#align option.map_uncurry Option.map_uncurry

/-!
### Algebraic replacement rules

A collection of lemmas to transfer associativity, commutativity, distributivity, ... of operations
to the associativity, commutativity, distributivity, ... of `option.map₂` of those operations.
The proof pattern is `map₂_lemma operation_lemma`. For example, `map₂_comm mul_comm` proves that
`map₂ (*) a b = map₂ (*) g f` in a `comm_semigroup`.
-/

/- warning: option.map₂_assoc -> Option.map₂_assoc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {ε : Type.{u5}} {ε' : Type.{u6}} {a : Option.{u1} α} {b : Option.{u2} β} {c : Option.{u3} γ} {f : δ -> γ -> ε} {g : α -> β -> δ} {f' : α -> ε' -> ε} {g' : β -> γ -> ε'}, (forall (a : α) (b : β) (c : γ), Eq.{succ u5} ε (f (g a b) c) (f' a (g' b c))) -> (Eq.{succ u5} (Option.{u5} ε) (Option.map₂.{u4, u3, u5} δ γ ε f (Option.map₂.{u1, u2, u4} α β δ g a b) c) (Option.map₂.{u1, u6, u5} α ε' ε f' a (Option.map₂.{u2, u3, u6} β γ ε' g' b c)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} {δ : Option.{u2} α} {ε : Option.{u1} β} {ε' : Option.{u3} γ} {a : Type.{u6}} {b : Type.{u5}} {c : Type.{u4}} {f : a -> γ -> b} {g : α -> β -> a} {f' : α -> c -> b} {g' : β -> γ -> c}, (forall (a : α) (b_1 : β) (c : γ), Eq.{succ u5} b (f (g a b_1) c) (f' a (g' b_1 c))) -> (Eq.{succ u5} (Option.{u5} b) (Option.map₂.{u6, u3, u5} a γ b f (Option.map₂.{u2, u1, u6} α β a g δ ε) ε') (Option.map₂.{u2, u4, u5} α c b f' δ (Option.map₂.{u1, u3, u4} β γ c g' ε ε')))
Case conversion may be inaccurate. Consider using '#align option.map₂_assoc Option.map₂_assocₓ'. -/
theorem map₂_assoc {f : δ → γ → ε} {g : α → β → δ} {f' : α → ε' → ε} {g' : β → γ → ε'}
    (h_assoc : ∀ a b c, f (g a b) c = f' a (g' b c)) :
    map₂ f (map₂ g a b) c = map₂ f' a (map₂ g' b c) := by
  cases a <;> cases b <;> cases c <;> simp [h_assoc]
#align option.map₂_assoc Option.map₂_assoc

/- warning: option.map₂_comm -> Option.map₂_comm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {f : α -> β -> γ} {a : Option.{u1} α} {b : Option.{u2} β} {g : β -> α -> γ}, (forall (a : α) (b : β), Eq.{succ u3} γ (f a b) (g b a)) -> (Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u1, u2, u3} α β γ f a b) (Option.map₂.{u2, u1, u3} β α γ g b a))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} {f : α -> β -> γ} {a : Option.{u2} α} {b : Option.{u1} β} {g : β -> α -> γ}, (forall (a : α) (b : β), Eq.{succ u3} γ (f a b) (g b a)) -> (Eq.{succ u3} (Option.{u3} γ) (Option.map₂.{u2, u1, u3} α β γ f a b) (Option.map₂.{u1, u2, u3} β α γ g b a))
Case conversion may be inaccurate. Consider using '#align option.map₂_comm Option.map₂_commₓ'. -/
theorem map₂_comm {g : β → α → γ} (h_comm : ∀ a b, f a b = g b a) : map₂ f a b = map₂ g b a := by
  cases a <;> cases b <;> simp [h_comm]
#align option.map₂_comm Option.map₂_comm

/- warning: option.map₂_left_comm -> Option.map₂_left_comm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {δ' : Type.{u5}} {ε : Type.{u6}} {a : Option.{u1} α} {b : Option.{u2} β} {c : Option.{u3} γ} {f : α -> δ -> ε} {g : β -> γ -> δ} {f' : α -> γ -> δ'} {g' : β -> δ' -> ε}, (forall (a : α) (b : β) (c : γ), Eq.{succ u6} ε (f a (g b c)) (g' b (f' a c))) -> (Eq.{succ u6} (Option.{u6} ε) (Option.map₂.{u1, u4, u6} α δ ε f a (Option.map₂.{u2, u3, u4} β γ δ g b c)) (Option.map₂.{u2, u5, u6} β δ' ε g' b (Option.map₂.{u1, u3, u5} α γ δ' f' a c)))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} {δ : Option.{u3} α} {δ' : Option.{u2} β} {ε : Option.{u1} γ} {a : Type.{u6}} {b : Type.{u5}} {c : Type.{u4}} {f : α -> a -> b} {g : β -> γ -> a} {f' : α -> γ -> c} {g' : β -> c -> b}, (forall (a : α) (b_1 : β) (c : γ), Eq.{succ u5} b (f a (g b_1 c)) (g' b_1 (f' a c))) -> (Eq.{succ u5} (Option.{u5} b) (Option.map₂.{u3, u6, u5} α a b f δ (Option.map₂.{u2, u1, u6} β γ a g δ' ε)) (Option.map₂.{u2, u4, u5} β c b g' δ' (Option.map₂.{u3, u1, u4} α γ c f' δ ε)))
Case conversion may be inaccurate. Consider using '#align option.map₂_left_comm Option.map₂_left_commₓ'. -/
theorem map₂_left_comm {f : α → δ → ε} {g : β → γ → δ} {f' : α → γ → δ'} {g' : β → δ' → ε}
    (h_left_comm : ∀ a b c, f a (g b c) = g' b (f' a c)) :
    map₂ f a (map₂ g b c) = map₂ g' b (map₂ f' a c) := by
  cases a <;> cases b <;> cases c <;> simp [h_left_comm]
#align option.map₂_left_comm Option.map₂_left_comm

/- warning: option.map₂_right_comm -> Option.map₂_right_comm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {δ' : Type.{u5}} {ε : Type.{u6}} {a : Option.{u1} α} {b : Option.{u2} β} {c : Option.{u3} γ} {f : δ -> γ -> ε} {g : α -> β -> δ} {f' : α -> γ -> δ'} {g' : δ' -> β -> ε}, (forall (a : α) (b : β) (c : γ), Eq.{succ u6} ε (f (g a b) c) (g' (f' a c) b)) -> (Eq.{succ u6} (Option.{u6} ε) (Option.map₂.{u4, u3, u6} δ γ ε f (Option.map₂.{u1, u2, u4} α β δ g a b) c) (Option.map₂.{u5, u2, u6} δ' β ε g' (Option.map₂.{u1, u3, u5} α γ δ' f' a c) b))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} {δ : Option.{u2} α} {δ' : Option.{u1} β} {ε : Option.{u3} γ} {a : Type.{u6}} {b : Type.{u5}} {c : Type.{u4}} {f : a -> γ -> b} {g : α -> β -> a} {f' : α -> γ -> c} {g' : c -> β -> b}, (forall (a : α) (b_1 : β) (c : γ), Eq.{succ u5} b (f (g a b_1) c) (g' (f' a c) b_1)) -> (Eq.{succ u5} (Option.{u5} b) (Option.map₂.{u6, u3, u5} a γ b f (Option.map₂.{u2, u1, u6} α β a g δ δ') ε) (Option.map₂.{u4, u1, u5} c β b g' (Option.map₂.{u2, u3, u4} α γ c f' δ ε) δ'))
Case conversion may be inaccurate. Consider using '#align option.map₂_right_comm Option.map₂_right_commₓ'. -/
theorem map₂_right_comm {f : δ → γ → ε} {g : α → β → δ} {f' : α → γ → δ'} {g' : δ' → β → ε}
    (h_right_comm : ∀ a b c, f (g a b) c = g' (f' a c) b) :
    map₂ f (map₂ g a b) c = map₂ g' (map₂ f' a c) b := by
  cases a <;> cases b <;> cases c <;> simp [h_right_comm]
#align option.map₂_right_comm Option.map₂_right_comm

/- warning: option.map_map₂_distrib -> Option.map_map₂_distrib is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {α' : Type.{u2}} {β : Type.{u3}} {β' : Type.{u4}} {γ : Type.{u5}} {δ : Type.{u6}} {f : α -> β -> γ} {a : Option.{u1} α} {b : Option.{u3} β} {g : γ -> δ} {f' : α' -> β' -> δ} {g₁ : α -> α'} {g₂ : β -> β'}, (forall (a : α) (b : β), Eq.{succ u6} δ (g (f a b)) (f' (g₁ a) (g₂ b))) -> (Eq.{succ u6} (Option.{u6} δ) (Option.map.{u5, u6} γ δ g (Option.map₂.{u1, u3, u5} α β γ f a b)) (Option.map₂.{u2, u4, u6} α' β' δ f' (Option.map.{u1, u2} α α' g₁ a) (Option.map.{u3, u4} β β' g₂ b)))
but is expected to have type
  forall {α : Type.{u2}} {α' : Type.{u1}} {β : Type.{u3}} {β' : α -> α' -> β} {γ : Option.{u2} α} {δ : Option.{u1} α'} {f : Type.{u6}} {a : Type.{u5}} {b : Type.{u4}} {g : β -> f} {f' : a -> b -> f} {g₁ : α -> a} {g₂ : α' -> b}, (forall (a : α) (b : α'), Eq.{succ u6} f (g (β' a b)) (f' (g₁ a) (g₂ b))) -> (Eq.{succ u6} (Option.{u6} f) (Option.map.{u3, u6} β f g (Option.map₂.{u2, u1, u3} α α' β β' γ δ)) (Option.map₂.{u5, u4, u6} a b f f' (Option.map.{u2, u5} α a g₁ γ) (Option.map.{u1, u4} α' b g₂ δ)))
Case conversion may be inaccurate. Consider using '#align option.map_map₂_distrib Option.map_map₂_distribₓ'. -/
theorem map_map₂_distrib {g : γ → δ} {f' : α' → β' → δ} {g₁ : α → α'} {g₂ : β → β'}
    (h_distrib : ∀ a b, g (f a b) = f' (g₁ a) (g₂ b)) :
    (map₂ f a b).map g = map₂ f' (a.map g₁) (b.map g₂) := by
  cases a <;> cases b <;> simp [h_distrib]
#align option.map_map₂_distrib Option.map_map₂_distrib

/-!
The following symmetric restatement are needed because unification has a hard time figuring all the
functions if you symmetrize on the spot. This is also how the other n-ary APIs do it.
-/

/- warning: option.map_map₂_distrib_left -> Option.map_map₂_distrib_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {α' : Type.{u2}} {β : Type.{u3}} {γ : Type.{u4}} {δ : Type.{u5}} {f : α -> β -> γ} {a : Option.{u1} α} {b : Option.{u3} β} {g : γ -> δ} {f' : α' -> β -> δ} {g' : α -> α'}, (forall (a : α) (b : β), Eq.{succ u5} δ (g (f a b)) (f' (g' a) b)) -> (Eq.{succ u5} (Option.{u5} δ) (Option.map.{u4, u5} γ δ g (Option.map₂.{u1, u3, u4} α β γ f a b)) (Option.map₂.{u2, u3, u5} α' β δ f' (Option.map.{u1, u2} α α' g' a) b))
but is expected to have type
  forall {α : Type.{u2}} {α' : Type.{u1}} {β : Type.{u3}} {γ : α -> α' -> β} {δ : Option.{u2} α} {f : Option.{u1} α'} {a : Type.{u5}} {b : Type.{u4}} {g : β -> a} {f' : b -> α' -> a} {g' : α -> b}, (forall (a_1 : α) (b : α'), Eq.{succ u5} a (g (γ a_1 b)) (f' (g' a_1) b)) -> (Eq.{succ u5} (Option.{u5} a) (Option.map.{u3, u5} β a g (Option.map₂.{u2, u1, u3} α α' β γ δ f)) (Option.map₂.{u4, u1, u5} b α' a f' (Option.map.{u2, u4} α b g' δ) f))
Case conversion may be inaccurate. Consider using '#align option.map_map₂_distrib_left Option.map_map₂_distrib_leftₓ'. -/
/-- Symmetric statement to `option.map₂_map_left_comm`. -/
theorem map_map₂_distrib_left {g : γ → δ} {f' : α' → β → δ} {g' : α → α'}
    (h_distrib : ∀ a b, g (f a b) = f' (g' a) b) : (map₂ f a b).map g = map₂ f' (a.map g') b := by
  cases a <;> cases b <;> simp [h_distrib]
#align option.map_map₂_distrib_left Option.map_map₂_distrib_left

/- warning: option.map_map₂_distrib_right -> Option.map_map₂_distrib_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {β' : Type.{u3}} {γ : Type.{u4}} {δ : Type.{u5}} {f : α -> β -> γ} {a : Option.{u1} α} {b : Option.{u2} β} {g : γ -> δ} {f' : α -> β' -> δ} {g' : β -> β'}, (forall (a : α) (b : β), Eq.{succ u5} δ (g (f a b)) (f' a (g' b))) -> (Eq.{succ u5} (Option.{u5} δ) (Option.map.{u4, u5} γ δ g (Option.map₂.{u1, u2, u4} α β γ f a b)) (Option.map₂.{u1, u3, u5} α β' δ f' a (Option.map.{u2, u3} β β' g' b)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {β' : Type.{u3}} {γ : α -> β -> β'} {δ : Option.{u2} α} {f : Option.{u1} β} {a : Type.{u5}} {b : Type.{u4}} {g : β' -> a} {f' : α -> b -> a} {g' : β -> b}, (forall (a_1 : α) (b : β), Eq.{succ u5} a (g (γ a_1 b)) (f' a_1 (g' b))) -> (Eq.{succ u5} (Option.{u5} a) (Option.map.{u3, u5} β' a g (Option.map₂.{u2, u1, u3} α β β' γ δ f)) (Option.map₂.{u2, u4, u5} α b a f' δ (Option.map.{u1, u4} β b g' f)))
Case conversion may be inaccurate. Consider using '#align option.map_map₂_distrib_right Option.map_map₂_distrib_rightₓ'. -/
/-- Symmetric statement to `option.map_map₂_right_comm`. -/
theorem map_map₂_distrib_right {g : γ → δ} {f' : α → β' → δ} {g' : β → β'}
    (h_distrib : ∀ a b, g (f a b) = f' a (g' b)) : (map₂ f a b).map g = map₂ f' a (b.map g') := by
  cases a <;> cases b <;> simp [h_distrib]
#align option.map_map₂_distrib_right Option.map_map₂_distrib_right

/- warning: option.map₂_map_left_comm -> Option.map₂_map_left_comm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {α' : Type.{u2}} {β : Type.{u3}} {γ : Type.{u4}} {δ : Type.{u5}} {a : Option.{u1} α} {b : Option.{u3} β} {f : α' -> β -> γ} {g : α -> α'} {f' : α -> β -> δ} {g' : δ -> γ}, (forall (a : α) (b : β), Eq.{succ u4} γ (f (g a) b) (g' (f' a b))) -> (Eq.{succ u4} (Option.{u4} γ) (Option.map₂.{u2, u3, u4} α' β γ f (Option.map.{u1, u2} α α' g a) b) (Option.map.{u5, u4} δ γ g' (Option.map₂.{u1, u3, u5} α β δ f' a b)))
but is expected to have type
  forall {α : Type.{u1}} {α' : Type.{u2}} {β : Type.{u3}} {γ : Option.{u1} α} {δ : Option.{u2} α'} {a : Type.{u5}} {b : Type.{u4}} {f : a -> α' -> β} {g : α -> a} {f' : α -> α' -> b} {g' : b -> β}, (forall (a : α) (b : α'), Eq.{succ u3} β (f (g a) b) (g' (f' a b))) -> (Eq.{succ u3} (Option.{u3} β) (Option.map₂.{u5, u2, u3} a α' β f (Option.map.{u1, u5} α a g γ) δ) (Option.map.{u4, u3} b β g' (Option.map₂.{u1, u2, u4} α α' b f' γ δ)))
Case conversion may be inaccurate. Consider using '#align option.map₂_map_left_comm Option.map₂_map_left_commₓ'. -/
/-- Symmetric statement to `option.map_map₂_distrib_left`. -/
theorem map₂_map_left_comm {f : α' → β → γ} {g : α → α'} {f' : α → β → δ} {g' : δ → γ}
    (h_left_comm : ∀ a b, f (g a) b = g' (f' a b)) : map₂ f (a.map g) b = (map₂ f' a b).map g' := by
  cases a <;> cases b <;> simp [h_left_comm]
#align option.map₂_map_left_comm Option.map₂_map_left_comm

/- warning: option.map_map₂_right_comm -> Option.map_map₂_right_comm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {β' : Type.{u3}} {γ : Type.{u4}} {δ : Type.{u5}} {a : Option.{u1} α} {b : Option.{u2} β} {f : α -> β' -> γ} {g : β -> β'} {f' : α -> β -> δ} {g' : δ -> γ}, (forall (a : α) (b : β), Eq.{succ u4} γ (f a (g b)) (g' (f' a b))) -> (Eq.{succ u4} (Option.{u4} γ) (Option.map₂.{u1, u3, u4} α β' γ f a (Option.map.{u2, u3} β β' g b)) (Option.map.{u5, u4} δ γ g' (Option.map₂.{u1, u2, u5} α β δ f' a b)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {β' : Type.{u3}} {γ : Option.{u2} α} {δ : Option.{u1} β} {a : Type.{u5}} {b : Type.{u4}} {f : α -> a -> β'} {g : β -> a} {f' : α -> β -> b} {g' : b -> β'}, (forall (a : α) (b : β), Eq.{succ u3} β' (f a (g b)) (g' (f' a b))) -> (Eq.{succ u3} (Option.{u3} β') (Option.map₂.{u2, u5, u3} α a β' f γ (Option.map.{u1, u5} β a g δ)) (Option.map.{u4, u3} b β' g' (Option.map₂.{u2, u1, u4} α β b f' γ δ)))
Case conversion may be inaccurate. Consider using '#align option.map_map₂_right_comm Option.map_map₂_right_commₓ'. -/
/-- Symmetric statement to `option.map_map₂_distrib_right`. -/
theorem map_map₂_right_comm {f : α → β' → γ} {g : β → β'} {f' : α → β → δ} {g' : δ → γ}
    (h_right_comm : ∀ a b, f a (g b) = g' (f' a b)) : map₂ f a (b.map g) = (map₂ f' a b).map g' :=
  by cases a <;> cases b <;> simp [h_right_comm]
#align option.map_map₂_right_comm Option.map_map₂_right_comm

/- warning: option.map_map₂_antidistrib -> Option.map_map₂_antidistrib is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {α' : Type.{u2}} {β : Type.{u3}} {β' : Type.{u4}} {γ : Type.{u5}} {δ : Type.{u6}} {f : α -> β -> γ} {a : Option.{u1} α} {b : Option.{u3} β} {g : γ -> δ} {f' : β' -> α' -> δ} {g₁ : β -> β'} {g₂ : α -> α'}, (forall (a : α) (b : β), Eq.{succ u6} δ (g (f a b)) (f' (g₁ b) (g₂ a))) -> (Eq.{succ u6} (Option.{u6} δ) (Option.map.{u5, u6} γ δ g (Option.map₂.{u1, u3, u5} α β γ f a b)) (Option.map₂.{u4, u2, u6} β' α' δ f' (Option.map.{u3, u4} β β' g₁ b) (Option.map.{u1, u2} α α' g₂ a)))
but is expected to have type
  forall {α : Type.{u2}} {α' : Type.{u1}} {β : Type.{u3}} {β' : α -> α' -> β} {γ : Option.{u2} α} {δ : Option.{u1} α'} {f : Type.{u6}} {a : Type.{u5}} {b : Type.{u4}} {g : β -> f} {f' : a -> b -> f} {g₁ : α' -> a} {g₂ : α -> b}, (forall (a : α) (b : α'), Eq.{succ u6} f (g (β' a b)) (f' (g₁ b) (g₂ a))) -> (Eq.{succ u6} (Option.{u6} f) (Option.map.{u3, u6} β f g (Option.map₂.{u2, u1, u3} α α' β β' γ δ)) (Option.map₂.{u5, u4, u6} a b f f' (Option.map.{u1, u5} α' a g₁ δ) (Option.map.{u2, u4} α b g₂ γ)))
Case conversion may be inaccurate. Consider using '#align option.map_map₂_antidistrib Option.map_map₂_antidistribₓ'. -/
theorem map_map₂_antidistrib {g : γ → δ} {f' : β' → α' → δ} {g₁ : β → β'} {g₂ : α → α'}
    (h_antidistrib : ∀ a b, g (f a b) = f' (g₁ b) (g₂ a)) :
    (map₂ f a b).map g = map₂ f' (b.map g₁) (a.map g₂) := by
  cases a <;> cases b <;> simp [h_antidistrib]
#align option.map_map₂_antidistrib Option.map_map₂_antidistrib

/- warning: option.map_map₂_antidistrib_left -> Option.map_map₂_antidistrib_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {β' : Type.{u3}} {γ : Type.{u4}} {δ : Type.{u5}} {f : α -> β -> γ} {a : Option.{u1} α} {b : Option.{u2} β} {g : γ -> δ} {f' : β' -> α -> δ} {g' : β -> β'}, (forall (a : α) (b : β), Eq.{succ u5} δ (g (f a b)) (f' (g' b) a)) -> (Eq.{succ u5} (Option.{u5} δ) (Option.map.{u4, u5} γ δ g (Option.map₂.{u1, u2, u4} α β γ f a b)) (Option.map₂.{u3, u1, u5} β' α δ f' (Option.map.{u2, u3} β β' g' b) a))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {β' : Type.{u3}} {γ : α -> β -> β'} {δ : Option.{u2} α} {f : Option.{u1} β} {a : Type.{u5}} {b : Type.{u4}} {g : β' -> a} {f' : b -> α -> a} {g' : β -> b}, (forall (a_1 : α) (b : β), Eq.{succ u5} a (g (γ a_1 b)) (f' (g' b) a_1)) -> (Eq.{succ u5} (Option.{u5} a) (Option.map.{u3, u5} β' a g (Option.map₂.{u2, u1, u3} α β β' γ δ f)) (Option.map₂.{u4, u2, u5} b α a f' (Option.map.{u1, u4} β b g' f) δ))
Case conversion may be inaccurate. Consider using '#align option.map_map₂_antidistrib_left Option.map_map₂_antidistrib_leftₓ'. -/
/-- Symmetric statement to `option.map₂_map_left_anticomm`. -/
theorem map_map₂_antidistrib_left {g : γ → δ} {f' : β' → α → δ} {g' : β → β'}
    (h_antidistrib : ∀ a b, g (f a b) = f' (g' b) a) : (map₂ f a b).map g = map₂ f' (b.map g') a :=
  by cases a <;> cases b <;> simp [h_antidistrib]
#align option.map_map₂_antidistrib_left Option.map_map₂_antidistrib_left

/- warning: option.map_map₂_antidistrib_right -> Option.map_map₂_antidistrib_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {α' : Type.{u2}} {β : Type.{u3}} {γ : Type.{u4}} {δ : Type.{u5}} {f : α -> β -> γ} {a : Option.{u1} α} {b : Option.{u3} β} {g : γ -> δ} {f' : β -> α' -> δ} {g' : α -> α'}, (forall (a : α) (b : β), Eq.{succ u5} δ (g (f a b)) (f' b (g' a))) -> (Eq.{succ u5} (Option.{u5} δ) (Option.map.{u4, u5} γ δ g (Option.map₂.{u1, u3, u4} α β γ f a b)) (Option.map₂.{u3, u2, u5} β α' δ f' b (Option.map.{u1, u2} α α' g' a)))
but is expected to have type
  forall {α : Type.{u2}} {α' : Type.{u1}} {β : Type.{u3}} {γ : α -> α' -> β} {δ : Option.{u2} α} {f : Option.{u1} α'} {a : Type.{u5}} {b : Type.{u4}} {g : β -> a} {f' : α' -> b -> a} {g' : α -> b}, (forall (a_1 : α) (b : α'), Eq.{succ u5} a (g (γ a_1 b)) (f' b (g' a_1))) -> (Eq.{succ u5} (Option.{u5} a) (Option.map.{u3, u5} β a g (Option.map₂.{u2, u1, u3} α α' β γ δ f)) (Option.map₂.{u1, u4, u5} α' b a f' f (Option.map.{u2, u4} α b g' δ)))
Case conversion may be inaccurate. Consider using '#align option.map_map₂_antidistrib_right Option.map_map₂_antidistrib_rightₓ'. -/
/-- Symmetric statement to `option.map_map₂_right_anticomm`. -/
theorem map_map₂_antidistrib_right {g : γ → δ} {f' : β → α' → δ} {g' : α → α'}
    (h_antidistrib : ∀ a b, g (f a b) = f' b (g' a)) : (map₂ f a b).map g = map₂ f' b (a.map g') :=
  by cases a <;> cases b <;> simp [h_antidistrib]
#align option.map_map₂_antidistrib_right Option.map_map₂_antidistrib_right

/- warning: option.map₂_map_left_anticomm -> Option.map₂_map_left_anticomm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {α' : Type.{u2}} {β : Type.{u3}} {γ : Type.{u4}} {δ : Type.{u5}} {a : Option.{u1} α} {b : Option.{u3} β} {f : α' -> β -> γ} {g : α -> α'} {f' : β -> α -> δ} {g' : δ -> γ}, (forall (a : α) (b : β), Eq.{succ u4} γ (f (g a) b) (g' (f' b a))) -> (Eq.{succ u4} (Option.{u4} γ) (Option.map₂.{u2, u3, u4} α' β γ f (Option.map.{u1, u2} α α' g a) b) (Option.map.{u5, u4} δ γ g' (Option.map₂.{u3, u1, u5} β α δ f' b a)))
but is expected to have type
  forall {α : Type.{u1}} {α' : Type.{u2}} {β : Type.{u3}} {γ : Option.{u1} α} {δ : Option.{u2} α'} {a : Type.{u5}} {b : Type.{u4}} {f : a -> α' -> β} {g : α -> a} {f' : α' -> α -> b} {g' : b -> β}, (forall (a : α) (b : α'), Eq.{succ u3} β (f (g a) b) (g' (f' b a))) -> (Eq.{succ u3} (Option.{u3} β) (Option.map₂.{u5, u2, u3} a α' β f (Option.map.{u1, u5} α a g γ) δ) (Option.map.{u4, u3} b β g' (Option.map₂.{u2, u1, u4} α' α b f' δ γ)))
Case conversion may be inaccurate. Consider using '#align option.map₂_map_left_anticomm Option.map₂_map_left_anticommₓ'. -/
/-- Symmetric statement to `option.map_map₂_antidistrib_left`. -/
theorem map₂_map_left_anticomm {f : α' → β → γ} {g : α → α'} {f' : β → α → δ} {g' : δ → γ}
    (h_left_anticomm : ∀ a b, f (g a) b = g' (f' b a)) :
    map₂ f (a.map g) b = (map₂ f' b a).map g' := by cases a <;> cases b <;> simp [h_left_anticomm]
#align option.map₂_map_left_anticomm Option.map₂_map_left_anticomm

/- warning: option.map_map₂_right_anticomm -> Option.map_map₂_right_anticomm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {β' : Type.{u3}} {γ : Type.{u4}} {δ : Type.{u5}} {a : Option.{u1} α} {b : Option.{u2} β} {f : α -> β' -> γ} {g : β -> β'} {f' : β -> α -> δ} {g' : δ -> γ}, (forall (a : α) (b : β), Eq.{succ u4} γ (f a (g b)) (g' (f' b a))) -> (Eq.{succ u4} (Option.{u4} γ) (Option.map₂.{u1, u3, u4} α β' γ f a (Option.map.{u2, u3} β β' g b)) (Option.map.{u5, u4} δ γ g' (Option.map₂.{u2, u1, u5} β α δ f' b a)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {β' : Type.{u3}} {γ : Option.{u2} α} {δ : Option.{u1} β} {a : Type.{u5}} {b : Type.{u4}} {f : α -> a -> β'} {g : β -> a} {f' : β -> α -> b} {g' : b -> β'}, (forall (a : α) (b : β), Eq.{succ u3} β' (f a (g b)) (g' (f' b a))) -> (Eq.{succ u3} (Option.{u3} β') (Option.map₂.{u2, u5, u3} α a β' f γ (Option.map.{u1, u5} β a g δ)) (Option.map.{u4, u3} b β' g' (Option.map₂.{u1, u2, u4} β α b f' δ γ)))
Case conversion may be inaccurate. Consider using '#align option.map_map₂_right_anticomm Option.map_map₂_right_anticommₓ'. -/
/-- Symmetric statement to `option.map_map₂_antidistrib_right`. -/
theorem map_map₂_right_anticomm {f : α → β' → γ} {g : β → β'} {f' : β → α → δ} {g' : δ → γ}
    (h_right_anticomm : ∀ a b, f a (g b) = g' (f' b a)) :
    map₂ f a (b.map g) = (map₂ f' b a).map g' := by cases a <;> cases b <;> simp [h_right_anticomm]
#align option.map_map₂_right_anticomm Option.map_map₂_right_anticomm

#print Option.map₂_left_identity /-
/-- If `a` is a left identity for a binary operation `f`, then `some a` is a left identity for
`option.map₂ f`. -/
theorem map₂_left_identity {f : α → β → β} {a : α} (h : ∀ b, f a b = b) (o : Option β) :
    map₂ f (some a) o = o := by
  cases o
  exacts[rfl, congr_arg some (h _)]
#align option.map₂_left_identity Option.map₂_left_identity
-/

/- warning: option.map₂_right_identity -> Option.map₂_right_identity is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : α -> β -> α} {b : β}, (forall (a : α), Eq.{succ u1} α (f a b) a) -> (forall (o : Option.{u1} α), Eq.{succ u1} (Option.{u1} α) (Option.map₂.{u1, u2, u1} α β α f o (Option.some.{u2} β b)) o)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : α -> β -> α} {b : β}, (forall (a : α), Eq.{succ u2} α (f a b) a) -> (forall (o : Option.{u2} α), Eq.{succ u2} (Option.{u2} α) (Option.map₂.{u2, u1, u2} α β α f o (Option.some.{u1} β b)) o)
Case conversion may be inaccurate. Consider using '#align option.map₂_right_identity Option.map₂_right_identityₓ'. -/
/-- If `b` is a right identity for a binary operation `f`, then `some b` is a right identity for
`option.map₂ f`. -/
theorem map₂_right_identity {f : α → β → α} {b : β} (h : ∀ a, f a b = a) (o : Option α) :
    map₂ f o (some b) = o := by simp [h, map₂]
#align option.map₂_right_identity Option.map₂_right_identity

end Option


/-
Copyright (c) 2021 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser

! This file was ported from Lean 3 source module algebra.module.hom
! leanprover-community/mathlib commit be24ec5de6701447e5df5ca75400ffee19d65659
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Module.Pi

/-!
# Bundled hom instances for module and multiplicative actions

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file defines instances for module, mul_action and related structures on bundled `_hom` types.

These are analogous to the instances in `algebra.module.pi`, but for bundled instead of unbundled
functions.
-/

variable {R S A B : Type _}

namespace AddMonoidHom

section

variable [Monoid R] [Monoid S] [AddMonoid A] [AddCommMonoid B]

variable [DistribMulAction R B] [DistribMulAction S B]

instance : DistribMulAction R (A →+ B)
    where
  smul r f :=
    { toFun := r • f
      map_zero' := by simp
      map_add' := fun x y => by simp [smul_add] }
  one_smul f := by simp
  mul_smul r s f := by simp [mul_smul]
  smul_add r f g := ext fun x => by simp [smul_add]
  smul_zero r := ext fun x => by simp [smul_zero]

/- warning: add_monoid_hom.coe_smul -> AddMonoidHom.coe_smul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} [_inst_1 : Monoid.{u1} R] [_inst_3 : AddMonoid.{u2} A] [_inst_4 : AddCommMonoid.{u3} B] [_inst_5 : DistribMulAction.{u1, u3} R B _inst_1 (AddCommMonoid.toAddMonoid.{u3} B _inst_4)] (r : R) (f : AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))), Eq.{succ (max u2 u3)} (A -> B) (coeFn.{succ (max u3 u2), succ (max u2 u3)} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (fun (_x : AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) => A -> B) (AddMonoidHom.hasCoeToFun.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (SMul.smul.{u1, max u3 u2} R (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (SMulZeroClass.toHasSmul.{u1, max u3 u2} R (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddZeroClass.toHasZero.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoid.toAddZeroClass.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoidHom.addCommMonoid.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) _inst_4)))) (DistribSMul.toSmulZeroClass.{u1, max u3 u2} R (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoid.toAddZeroClass.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoidHom.addCommMonoid.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) _inst_4))) (DistribMulAction.toDistribSMul.{u1, max u3 u2} R (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) _inst_1 (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoidHom.addCommMonoid.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) _inst_4)) (AddMonoidHom.distribMulAction.{u1, u2, u3} R A B _inst_1 _inst_3 _inst_4 _inst_5)))) r f)) (SMul.smul.{u1, max u2 u3} R (A -> B) (Function.hasSMul.{u2, u1, u3} A R B (SMulZeroClass.toHasSmul.{u1, u3} R B (AddZeroClass.toHasZero.{u3} B (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (DistribSMul.toSmulZeroClass.{u1, u3} R B (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4)) (DistribMulAction.toDistribSMul.{u1, u3} R B _inst_1 (AddCommMonoid.toAddMonoid.{u3} B _inst_4) _inst_5)))) r (coeFn.{max (succ u3) (succ u2), max (succ u2) (succ u3)} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (fun (_x : AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) => A -> B) (AddMonoidHom.hasCoeToFun.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) f))
but is expected to have type
  forall {R : Type.{u1}} {A : Type.{u3}} {B : Type.{u2}} [_inst_1 : Monoid.{u1} R] [_inst_3 : AddMonoid.{u3} A] [_inst_4 : AddCommMonoid.{u2} B] [_inst_5 : DistribMulAction.{u1, u2} R B _inst_1 (AddCommMonoid.toAddMonoid.{u2} B _inst_4)] (r : R) (f : AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))), Eq.{max (succ u3) (succ u2)} (forall (ᾰ : A), (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) ᾰ) (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A (fun (_x : A) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) _x) (AddHomClass.toFunLike.{max u3 u2, u3, u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A B (AddZeroClass.toAdd.{u3} A (AddMonoid.toAddZeroClass.{u3} A _inst_3)) (AddZeroClass.toAdd.{u2} B (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHomClass.toAddHomClass.{max u3 u2, u3, u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4)) (AddMonoidHom.addMonoidHomClass.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))))) (HSMul.hSMul.{u1, max u3 u2, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (instHSMul.{u1, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (SMulZeroClass.toSMul.{u1, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (instZeroAddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (DistribSMul.toSMulZeroClass.{u1, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoid.toAddZeroClass.{max u3 u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHom.addCommMonoid.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) _inst_4))) (DistribMulAction.toDistribSMul.{u1, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) _inst_1 (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHom.addCommMonoid.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) _inst_4)) (AddMonoidHom.distribMulAction.{u1, u3, u2} R A B _inst_1 _inst_3 _inst_4 _inst_5))))) r f)) (HSMul.hSMul.{u1, max u3 u2, max u3 u2} R (forall (a : A), (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) a) (forall (ᾰ : A), (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) ᾰ) (instHSMul.{u1, max u3 u2} R (forall (a : A), (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) a) (Pi.instSMul.{u3, u2, u1} A R (fun (a : A) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) a) (fun (i : A) => SMulZeroClass.toSMul.{u1, u2} R ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) i) (AddMonoid.toZero.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) i) (AddCommMonoid.toAddMonoid.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) i) _inst_4)) (DistribSMul.toSMulZeroClass.{u1, u2} R ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) i) (AddMonoid.toAddZeroClass.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) i) (AddCommMonoid.toAddMonoid.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) i) _inst_4)) (DistribMulAction.toDistribSMul.{u1, u2} R ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) i) _inst_1 (AddCommMonoid.toAddMonoid.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) i) _inst_4) _inst_5))))) r (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A (fun (_x : A) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) _x) (AddHomClass.toFunLike.{max u3 u2, u3, u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A B (AddZeroClass.toAdd.{u3} A (AddMonoid.toAddZeroClass.{u3} A _inst_3)) (AddZeroClass.toAdd.{u2} B (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHomClass.toAddHomClass.{max u3 u2, u3, u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4)) (AddMonoidHom.addMonoidHomClass.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))))) f))
Case conversion may be inaccurate. Consider using '#align add_monoid_hom.coe_smul AddMonoidHom.coe_smulₓ'. -/
@[simp]
theorem coe_smul (r : R) (f : A →+ B) : ⇑(r • f) = r • f :=
  rfl
#align add_monoid_hom.coe_smul AddMonoidHom.coe_smul

/- warning: add_monoid_hom.smul_apply -> AddMonoidHom.smul_apply is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {B : Type.{u3}} [_inst_1 : Monoid.{u1} R] [_inst_3 : AddMonoid.{u2} A] [_inst_4 : AddCommMonoid.{u3} B] [_inst_5 : DistribMulAction.{u1, u3} R B _inst_1 (AddCommMonoid.toAddMonoid.{u3} B _inst_4)] (r : R) (f : AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (x : A), Eq.{succ u3} B (coeFn.{max (succ u3) (succ u2), max (succ u2) (succ u3)} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (fun (_x : AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) => A -> B) (AddMonoidHom.hasCoeToFun.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (SMul.smul.{u1, max u3 u2} R (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (SMulZeroClass.toHasSmul.{u1, max u3 u2} R (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddZeroClass.toHasZero.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoid.toAddZeroClass.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoidHom.addCommMonoid.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) _inst_4)))) (DistribSMul.toSmulZeroClass.{u1, max u3 u2} R (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoid.toAddZeroClass.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoidHom.addCommMonoid.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) _inst_4))) (DistribMulAction.toDistribSMul.{u1, max u3 u2} R (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) _inst_1 (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (AddMonoidHom.addCommMonoid.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) _inst_4)) (AddMonoidHom.distribMulAction.{u1, u2, u3} R A B _inst_1 _inst_3 _inst_4 _inst_5)))) r f) x) (SMul.smul.{u1, u3} R B (SMulZeroClass.toHasSmul.{u1, u3} R B (AddZeroClass.toHasZero.{u3} B (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (DistribSMul.toSmulZeroClass.{u1, u3} R B (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4)) (DistribMulAction.toDistribSMul.{u1, u3} R B _inst_1 (AddCommMonoid.toAddMonoid.{u3} B _inst_4) _inst_5))) r (coeFn.{max (succ u3) (succ u2), max (succ u2) (succ u3)} (AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) (fun (_x : AddMonoidHom.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) => A -> B) (AddMonoidHom.hasCoeToFun.{u2, u3} A B (AddMonoid.toAddZeroClass.{u2} A _inst_3) (AddMonoid.toAddZeroClass.{u3} B (AddCommMonoid.toAddMonoid.{u3} B _inst_4))) f x))
but is expected to have type
  forall {R : Type.{u1}} {A : Type.{u3}} {B : Type.{u2}} [_inst_1 : Monoid.{u1} R] [_inst_3 : AddMonoid.{u3} A] [_inst_4 : AddCommMonoid.{u2} B] [_inst_5 : DistribMulAction.{u1, u2} R B _inst_1 (AddCommMonoid.toAddMonoid.{u2} B _inst_4)] (r : R) (f : AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (x : A), Eq.{succ u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A (fun (_x : A) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) _x) (AddHomClass.toFunLike.{max u3 u2, u3, u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A B (AddZeroClass.toAdd.{u3} A (AddMonoid.toAddZeroClass.{u3} A _inst_3)) (AddZeroClass.toAdd.{u2} B (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHomClass.toAddHomClass.{max u3 u2, u3, u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4)) (AddMonoidHom.addMonoidHomClass.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))))) (HSMul.hSMul.{u1, max u3 u2, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (instHSMul.{u1, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (SMulZeroClass.toSMul.{u1, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (instZeroAddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (DistribSMul.toSMulZeroClass.{u1, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoid.toAddZeroClass.{max u3 u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHom.addCommMonoid.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) _inst_4))) (DistribMulAction.toDistribSMul.{u1, max u3 u2} R (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) _inst_1 (AddCommMonoid.toAddMonoid.{max u3 u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHom.addCommMonoid.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) _inst_4)) (AddMonoidHom.distribMulAction.{u1, u3, u2} R A B _inst_1 _inst_3 _inst_4 _inst_5))))) r f) x) (HSMul.hSMul.{u1, u2, u2} R ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) (instHSMul.{u1, u2} R ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) (SMulZeroClass.toSMul.{u1, u2} R ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) (AddMonoid.toZero.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) (AddCommMonoid.toAddMonoid.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) _inst_4)) (DistribSMul.toSMulZeroClass.{u1, u2} R ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) (AddMonoid.toAddZeroClass.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) (AddCommMonoid.toAddMonoid.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) _inst_4)) (DistribMulAction.toDistribSMul.{u1, u2} R ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) _inst_1 (AddCommMonoid.toAddMonoid.{u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) x) _inst_4) _inst_5)))) r (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A (fun (_x : A) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : A) => B) _x) (AddHomClass.toFunLike.{max u3 u2, u3, u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A B (AddZeroClass.toAdd.{u3} A (AddMonoid.toAddZeroClass.{u3} A _inst_3)) (AddZeroClass.toAdd.{u2} B (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) (AddMonoidHomClass.toAddHomClass.{max u3 u2, u3, u2} (AddMonoidHom.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))) A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4)) (AddMonoidHom.addMonoidHomClass.{u3, u2} A B (AddMonoid.toAddZeroClass.{u3} A _inst_3) (AddMonoid.toAddZeroClass.{u2} B (AddCommMonoid.toAddMonoid.{u2} B _inst_4))))) f x))
Case conversion may be inaccurate. Consider using '#align add_monoid_hom.smul_apply AddMonoidHom.smul_applyₓ'. -/
theorem smul_apply (r : R) (f : A →+ B) (x : A) : (r • f) x = r • f x :=
  rfl
#align add_monoid_hom.smul_apply AddMonoidHom.smul_apply

instance [SMulCommClass R S B] : SMulCommClass R S (A →+ B) :=
  ⟨fun a b f => ext fun x => smul_comm _ _ _⟩

instance [SMul R S] [IsScalarTower R S B] : IsScalarTower R S (A →+ B) :=
  ⟨fun a b f => ext fun x => smul_assoc _ _ _⟩

instance [DistribMulAction Rᵐᵒᵖ B] [IsCentralScalar R B] : IsCentralScalar R (A →+ B) :=
  ⟨fun a b => ext fun x => op_smul_eq_smul _ _⟩

end

instance [Semiring R] [AddMonoid A] [AddCommMonoid B] [Module R B] : Module R (A →+ B) :=
  {
    AddMonoidHom.distribMulAction with
    add_smul := fun r s x => ext fun y => by simp [add_smul]
    zero_smul := fun x => ext fun y => by simp [zero_smul] }

end AddMonoidHom


/-
Copyright (c) 2022 Joël Riou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joël Riou
-/

import for_mathlib.algebra.homology.twist_cocycle

noncomputable theory

open category_theory category_theory.category category_theory.limits category_theory.preadditive

namespace cochain_complex

namespace lifting

open hom_complex

variables {C : Type*} [category C] [abelian C]

variables {A B K X Y : cochain_complex C ℤ} {n : ℤ} {z : cocycle B A 1} {f : A ⟶ X} (g : twist z ⟶ Y) {p : X ⟶ Y} {j : K ⟶ X}
  (sq : comm_sq f (twist.inr z) p g) (l : Π (q : ℤ), comm_sq.lift_struct ((homological_complex.eval C _ q).map_comm_sq sq))
  (hpj : is_termwise_kernel j p)

@[simp]
def φ : cochain B Y 0 := cochain.comp (twist.inl z (zero_add 1)) (cochain.of_hom g) (zero_add 0).symm

lemma dφ : δ 0 1 (φ g) = cochain.comp ↑z (cochain.of_hom (twist.inr z ≫ g)) (add_zero 1).symm :=
by simp only [φ, add_left_eq_self, δ_comp_of_second_is_zero_cochain,
  cocycle.δ_cochain_of_hom, cochain.comp_zero, twist.δ_inl,
  cochain.comp_assoc_of_third_is_zero_cochain, cochain.of_hom_comp]

variable {g}

@[simp]
def L : cochain B X 0 := cochain.comp (twist.inl z (zero_add 1))
  (cochain.of_homs (λ q, (l q).l)) (zero_add 0).symm

include sq l

@[simps]
def obs₀ : cocycle B X 1 :=
cocycle.mk (δ 0 1 (L sq l) - cochain.comp ↑z (cochain.of_hom f) (add_zero 1).symm) 2 rfl
(by simp only [δ_sub, δδ, zero_sub, δ_comp_of_second_is_zero_cochain _ _ 2 rfl, neg_zero,
    cocycle.δ_eq_zero, cocycle.δ_cochain_of_hom, cochain.comp_zero, cochain.zero_comp, add_zero])

def obs : cocycle B K 1 :=
cocycle.lift_to_kernel (obs₀ sq l) hpj
begin
  have eq₁ : cochain.comp (cochain.of_homs (λ (q : ℤ), (l q).l))
    (cochain.of_hom p) (zero_add 0).symm = cochain.of_hom g,
  { simp only [cochain.of_hom, cochain.of_homs_comp],
    congr' 1,
    ext1 q,
    exact (l q).fac_right, },
  have eq₂ : cochain.comp (δ 0 1 (cochain.of_homs (λ (q : ℤ), (l q).l))) (cochain.of_hom p)
    (add_zero 1).symm = 0,
  { suffices : δ 0 1 (cochain.comp (cochain.of_homs (λ (q : ℤ), (l q).l)) (cochain.of_hom p)
      (zero_add 0).symm) = 0,
    { simpa only [δ_comp_of_second_is_zero_cochain _ _ _ (zero_add 1),
        zero_add, cocycle.δ_cochain_of_hom, cochain.comp_zero] using this, },
    simp only [eq₁, cocycle.δ_cochain_of_hom], },
  simp only [obs₀, cochain.sub_comp, cocycle.mk_coe, cochain.add_comp,
    cochain.comp_assoc_of_third_is_zero_cochain, ← cochain.of_hom_comp, sq.w, L,
    δ_comp_of_second_is_zero_cochain _ _ _ (zero_add 1),
    eq₂, cochain.comp_zero, zero_add,
    eq₁, ← dφ g, φ, cocycle.δ_cochain_of_hom, sub_self],
end

@[simp]
lemma obs_comp :
  cochain.comp (obs sq l hpj : cochain B K 1) (cochain.of_hom j)
    (add_zero 1).symm = ↑(obs₀ sq l) :=
by apply cocycle.lift_to_kernel_comp

variables (w : cochain B K 0) (hw : δ 0 1 w = ↑(obs sq l hpj))
variable (j)

@[simp]
def F : cochain B X 0 := L sq l - cochain.comp w (cochain.of_hom j) (add_zero 0).symm

variable {j}
include hw

lemma dF :
  δ 0 1 (F j sq l w) =
    (z : cochain B A 1).comp (cochain.of_hom f) (add_zero 1).symm :=
by simp only [F, δ_sub, δ_comp_of_second_is_zero_cochain _ _ _ (zero_add 1),
  cocycle.δ_cochain_of_hom, cochain.comp_zero, zero_add, hw, obs_comp, obs₀,
  cocycle.mk_coe, sub_sub_cancel]

lemma lift_of_coboundary : comm_sq.lift_struct sq :=
{ l := twist.desc z (F j sq l w) f (zero_add 1) (dF sq l hpj w hw),
  fac_left' := by apply twist.inr_comp_desc,
  fac_right' := begin
    apply cochain.of_hom_injective,
    simp only [twist.desc, cochain.of_hom_comp,cocycle.cochain_of_hom_hom_of_eq_coe,
      twist.desc_hom_as_cocycle_coe,
      twist.cochain_ext z _ _ (zero_add 1) (zero_add 0).symm,
      ← cochain.comp_assoc_of_third_is_zero_cochain, twist.inl_comp_desc_cochain,
      twist.inr_comp_desc_cochain],
    split,
    { ext i,
      have hl := (l i).fac_right,
      simp only [homological_complex.eval_map] at hl,
      simp only [F, L, hl, cochain.sub_comp, cochain.comp_assoc_of_third_is_zero_cochain,
        cochain.sub_v, cochain.comp_zero_cochain, cochain.of_homs_v, cochain.of_hom_v,
        sub_eq_self, hpj.zero i, comp_zero], },
    { simp only [← cochain.of_hom_comp, sq.w], },
  end }

end lifting

end cochain_complex


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Johannes Hölzl
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.instances.nnreal
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

/-!
# Extended non-negative reals
-/

namespace ennreal

/-- Topology on `ennreal`.

Note: this is different from the `emetric_space` topology. The `emetric_space` topology has
`is_open {⊤}`, while this topology doesn't have singleton elements. -/
protected instance topological_space : topological_space ennreal := preorder.topology ennreal

protected instance order_topology : order_topology ennreal := order_topology.mk rfl

protected instance t2_space : t2_space ennreal := regular_space.t2_space ennreal

protected instance topological_space.second_countable_topology :
    topological_space.second_countable_topology ennreal :=
  sorry

theorem embedding_coe : embedding coe := sorry

theorem is_open_ne_top : is_open (set_of fun (a : ennreal) => a ≠ ⊤) := is_open_ne

theorem is_open_Ico_zero {b : ennreal} : is_open (set.Ico 0 b) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (is_open (set.Ico 0 b))) ennreal.Ico_eq_Iio)) is_open_Iio

theorem coe_range_mem_nhds {r : nnreal} : set.range coe ∈ nhds ↑r := sorry

theorem tendsto_coe {α : Type u_1} {f : filter α} {m : α → nnreal} {a : nnreal} :
    filter.tendsto (fun (a : α) => ↑(m a)) f (nhds ↑a) ↔ filter.tendsto m f (nhds a) :=
  iff.symm (embedding.tendsto_nhds_iff embedding_coe)

theorem continuous_coe : continuous coe := embedding.continuous embedding_coe

theorem continuous_coe_iff {α : Type u_1} [topological_space α] {f : α → nnreal} :
    (continuous fun (a : α) => ↑(f a)) ↔ continuous f :=
  iff.symm (embedding.continuous_iff embedding_coe)

theorem nhds_coe {r : nnreal} : nhds ↑r = filter.map coe (nhds r) := sorry

theorem nhds_coe_coe {r : nnreal} {p : nnreal} :
    nhds (↑r, ↑p) =
        filter.map (fun (p : nnreal × nnreal) => (↑(prod.fst p), ↑(prod.snd p))) (nhds (r, p)) :=
  sorry

theorem continuous_of_real : continuous ennreal.of_real :=
  continuous.comp (iff.mpr continuous_coe_iff continuous_id) nnreal.continuous_of_real

theorem tendsto_of_real {α : Type u_1} {f : filter α} {m : α → ℝ} {a : ℝ}
    (h : filter.tendsto m f (nhds a)) :
    filter.tendsto (fun (a : α) => ennreal.of_real (m a)) f (nhds (ennreal.of_real a)) :=
  filter.tendsto.comp (continuous.tendsto continuous_of_real a) h

theorem tendsto_to_nnreal {a : ennreal} :
    a ≠ ⊤ → filter.tendsto ennreal.to_nnreal (nhds a) (nhds (ennreal.to_nnreal a)) :=
  sorry

theorem continuous_on_to_nnreal :
    continuous_on ennreal.to_nnreal (set_of fun (a : ennreal) => a ≠ ⊤) :=
  sorry

theorem tendsto_to_real {a : ennreal} :
    a ≠ ⊤ → filter.tendsto ennreal.to_real (nhds a) (nhds (ennreal.to_real a)) :=
  fun (ha : a ≠ ⊤) =>
    filter.tendsto.comp (iff.mpr nnreal.tendsto_coe filter.tendsto_id) (tendsto_to_nnreal ha)

/-- The set of finite `ennreal` numbers is homeomorphic to `ℝ≥0`. -/
def ne_top_homeomorph_nnreal : ↥(set_of fun (a : ennreal) => a ≠ ⊤) ≃ₜ nnreal :=
  homeomorph.mk
    (equiv.mk (equiv.to_fun ne_top_equiv_nnreal) (equiv.inv_fun ne_top_equiv_nnreal) sorry sorry)

/-- The set of finite `ennreal` numbers is homeomorphic to `ℝ≥0`. -/
def lt_top_homeomorph_nnreal : ↥(set_of fun (a : ennreal) => a < ⊤) ≃ₜ nnreal :=
  homeomorph.trans (homeomorph.set_congr sorry) ne_top_homeomorph_nnreal

theorem nhds_top :
    nhds ⊤ = infi fun (a : ennreal) => infi fun (H : a ≠ ⊤) => filter.principal (set.Ioi a) :=
  sorry

theorem nhds_top' : nhds ⊤ = infi fun (r : nnreal) => filter.principal (set.Ioi ↑r) :=
  Eq.trans nhds_top (infi_ne_top fun (a : ennreal) => filter.principal (set.Ioi a))

theorem tendsto_nhds_top_iff_nnreal {α : Type u_1} {m : α → ennreal} {f : filter α} :
    filter.tendsto m f (nhds ⊤) ↔ ∀ (x : nnreal), filter.eventually (fun (a : α) => ↑x < m a) f :=
  sorry

theorem tendsto_nhds_top_iff_nat {α : Type u_1} {m : α → ennreal} {f : filter α} :
    filter.tendsto m f (nhds ⊤) ↔ ∀ (n : ℕ), filter.eventually (fun (a : α) => ↑n < m a) f :=
  sorry

theorem tendsto_nhds_top {α : Type u_1} {m : α → ennreal} {f : filter α}
    (h : ∀ (n : ℕ), filter.eventually (fun (a : α) => ↑n < m a) f) : filter.tendsto m f (nhds ⊤) :=
  iff.mpr tendsto_nhds_top_iff_nat h

theorem tendsto_nat_nhds_top : filter.tendsto (fun (n : ℕ) => ↑n) filter.at_top (nhds ⊤) := sorry

@[simp] theorem tendsto_coe_nhds_top {α : Type u_1} {f : α → nnreal} {l : filter α} :
    filter.tendsto (fun (x : α) => ↑(f x)) l (nhds ⊤) ↔ filter.tendsto f l filter.at_top :=
  sorry

theorem nhds_zero :
    nhds 0 = infi fun (a : ennreal) => infi fun (H : a ≠ 0) => filter.principal (set.Iio a) :=
  sorry

instance nhds_within_Ioi_coe_ne_bot {r : nnreal} : filter.ne_bot (nhds_within (↑r) (set.Ioi ↑r)) :=
  nhds_within_Ioi_self_ne_bot' coe_lt_top

instance nhds_within_Ioi_zero_ne_bot : filter.ne_bot (nhds_within 0 (set.Ioi 0)) :=
  nhds_within_Ioi_coe_ne_bot

-- using Icc because

-- • don't have 'Ioo (x - ε) (x + ε) ∈ 𝓝 x' unless x > 0

-- • (x - y ≤ ε ↔ x ≤ ε + y) is true, while (x - y < ε ↔ x < ε + y) is not

theorem Icc_mem_nhds {x : ennreal} {ε : ennreal} :
    x ≠ ⊤ → 0 < ε → set.Icc (x - ε) (x + ε) ∈ nhds x :=
  sorry

theorem nhds_of_ne_top {x : ennreal} :
    x ≠ ⊤ →
        nhds x =
          infi
            fun (ε : ennreal) =>
              infi fun (H : ε > 0) => filter.principal (set.Icc (x - ε) (x + ε)) :=
  sorry

/-- Characterization of neighborhoods for `ennreal` numbers. See also `tendsto_order`
for a version with strict inequalities. -/
protected theorem tendsto_nhds {α : Type u_1} {f : filter α} {u : α → ennreal} {a : ennreal}
    (ha : a ≠ ⊤) :
    filter.tendsto u f (nhds a) ↔
        ∀ (ε : ennreal),
          ε > 0 → filter.eventually (fun (x : α) => u x ∈ set.Icc (a - ε) (a + ε)) f :=
  sorry

protected theorem tendsto_at_top {β : Type u_2} [Nonempty β] [semilattice_sup β] {f : β → ennreal}
    {a : ennreal} (ha : a ≠ ⊤) :
    filter.tendsto f filter.at_top (nhds a) ↔
        ∀ (ε : ennreal), ε > 0 → ∃ (N : β), ∀ (n : β), n ≥ N → f n ∈ set.Icc (a - ε) (a + ε) :=
  sorry

protected instance has_continuous_add : has_continuous_add ennreal := sorry

protected theorem tendsto_mul {a : ennreal} {b : ennreal} (ha : a ≠ 0 ∨ b ≠ ⊤)
    (hb : b ≠ 0 ∨ a ≠ ⊤) :
    filter.tendsto (fun (p : ennreal × ennreal) => prod.fst p * prod.snd p) (nhds (a, b))
        (nhds (a * b)) :=
  sorry

protected theorem tendsto.mul {α : Type u_1} {f : filter α} {ma : α → ennreal} {mb : α → ennreal}
    {a : ennreal} {b : ennreal} (hma : filter.tendsto ma f (nhds a)) (ha : a ≠ 0 ∨ b ≠ ⊤)
    (hmb : filter.tendsto mb f (nhds b)) (hb : b ≠ 0 ∨ a ≠ ⊤) :
    filter.tendsto (fun (a : α) => ma a * mb a) f (nhds (a * b)) :=
  sorry

protected theorem tendsto.const_mul {α : Type u_1} {f : filter α} {m : α → ennreal} {a : ennreal}
    {b : ennreal} (hm : filter.tendsto m f (nhds b)) (hb : b ≠ 0 ∨ a ≠ ⊤) :
    filter.tendsto (fun (b : α) => a * m b) f (nhds (a * b)) :=
  sorry

protected theorem tendsto.mul_const {α : Type u_1} {f : filter α} {m : α → ennreal} {a : ennreal}
    {b : ennreal} (hm : filter.tendsto m f (nhds a)) (ha : a ≠ 0 ∨ b ≠ ⊤) :
    filter.tendsto (fun (x : α) => m x * b) f (nhds (a * b)) :=
  sorry

protected theorem continuous_at_const_mul {a : ennreal} {b : ennreal} (h : a ≠ ⊤ ∨ b ≠ 0) :
    continuous_at (Mul.mul a) b :=
  tendsto.const_mul filter.tendsto_id (or.symm h)

protected theorem continuous_at_mul_const {a : ennreal} {b : ennreal} (h : a ≠ ⊤ ∨ b ≠ 0) :
    continuous_at (fun (x : ennreal) => x * a) b :=
  tendsto.mul_const filter.tendsto_id (or.symm h)

protected theorem continuous_const_mul {a : ennreal} (ha : a ≠ ⊤) : continuous (Mul.mul a) :=
  iff.mpr continuous_iff_continuous_at
    fun (x : ennreal) => ennreal.continuous_at_const_mul (Or.inl ha)

protected theorem continuous_mul_const {a : ennreal} (ha : a ≠ ⊤) :
    continuous fun (x : ennreal) => x * a :=
  iff.mpr continuous_iff_continuous_at
    fun (x : ennreal) => ennreal.continuous_at_mul_const (Or.inl ha)

theorem le_of_forall_lt_one_mul_le {x : ennreal} {y : ennreal}
    (h : ∀ (a : ennreal), a < 1 → a * x ≤ y) : x ≤ y :=
  sorry

theorem infi_mul_left {ι : Sort u_1} [Nonempty ι] {f : ι → ennreal} {a : ennreal}
    (h : a = ⊤ → (infi fun (i : ι) => f i) = 0 → ∃ (i : ι), f i = 0) :
    (infi fun (i : ι) => a * f i) = a * infi fun (i : ι) => f i :=
  sorry

theorem infi_mul_right {ι : Sort u_1} [Nonempty ι] {f : ι → ennreal} {a : ennreal}
    (h : a = ⊤ → (infi fun (i : ι) => f i) = 0 → ∃ (i : ι), f i = 0) :
    (infi fun (i : ι) => f i * a) = (infi fun (i : ι) => f i) * a :=
  sorry

protected theorem continuous_inv : continuous has_inv.inv := sorry

@[simp] protected theorem tendsto_inv_iff {α : Type u_1} {f : filter α} {m : α → ennreal}
    {a : ennreal} :
    filter.tendsto (fun (x : α) => m x⁻¹) f (nhds (a⁻¹)) ↔ filter.tendsto m f (nhds a) :=
  sorry

protected theorem tendsto.div {α : Type u_1} {f : filter α} {ma : α → ennreal} {mb : α → ennreal}
    {a : ennreal} {b : ennreal} (hma : filter.tendsto ma f (nhds a)) (ha : a ≠ 0 ∨ b ≠ 0)
    (hmb : filter.tendsto mb f (nhds b)) (hb : b ≠ ⊤ ∨ a ≠ ⊤) :
    filter.tendsto (fun (a : α) => ma a / mb a) f (nhds (a / b)) :=
  sorry

protected theorem tendsto.const_div {α : Type u_1} {f : filter α} {m : α → ennreal} {a : ennreal}
    {b : ennreal} (hm : filter.tendsto m f (nhds b)) (hb : b ≠ ⊤ ∨ a ≠ ⊤) :
    filter.tendsto (fun (b : α) => a / m b) f (nhds (a / b)) :=
  sorry

protected theorem tendsto.div_const {α : Type u_1} {f : filter α} {m : α → ennreal} {a : ennreal}
    {b : ennreal} (hm : filter.tendsto m f (nhds a)) (ha : a ≠ 0 ∨ b ≠ 0) :
    filter.tendsto (fun (x : α) => m x / b) f (nhds (a / b)) :=
  sorry

protected theorem tendsto_inv_nat_nhds_zero :
    filter.tendsto (fun (n : ℕ) => ↑n⁻¹) filter.at_top (nhds 0) :=
  inv_top ▸ iff.mpr ennreal.tendsto_inv_iff tendsto_nat_nhds_top

theorem bsupr_add {a : ennreal} {ι : Type u_1} {s : set ι} (hs : set.nonempty s) {f : ι → ennreal} :
    (supr fun (i : ι) => supr fun (H : i ∈ s) => f i) + a =
        supr fun (i : ι) => supr fun (H : i ∈ s) => f i + a :=
  sorry

theorem Sup_add {a : ennreal} {s : set ennreal} (hs : set.nonempty s) :
    Sup s + a = supr fun (b : ennreal) => supr fun (H : b ∈ s) => b + a :=
  sorry

theorem supr_add {a : ennreal} {ι : Sort u_1} {s : ι → ennreal} [h : Nonempty ι] :
    supr s + a = supr fun (b : ι) => s b + a :=
  sorry

theorem add_supr {a : ennreal} {ι : Sort u_1} {s : ι → ennreal} [h : Nonempty ι] :
    a + supr s = supr fun (b : ι) => a + s b :=
  sorry

theorem supr_add_supr {ι : Sort u_1} {f : ι → ennreal} {g : ι → ennreal}
    (h : ∀ (i j : ι), ∃ (k : ι), f i + g j ≤ f k + g k) :
    supr f + supr g = supr fun (a : ι) => f a + g a :=
  sorry

theorem supr_add_supr_of_monotone {ι : Type u_1} [semilattice_sup ι] {f : ι → ennreal}
    {g : ι → ennreal} (hf : monotone f) (hg : monotone g) :
    supr f + supr g = supr fun (a : ι) => f a + g a :=
  supr_add_supr
    fun (i j : ι) => Exists.intro (i ⊔ j) (add_le_add (hf le_sup_left) (hg le_sup_right))

theorem finset_sum_supr_nat {α : Type u_1} {ι : Type u_2} [semilattice_sup ι] {s : finset α}
    {f : α → ι → ennreal} (hf : ∀ (a : α), monotone (f a)) :
    (finset.sum s fun (a : α) => supr (f a)) =
        supr fun (n : ι) => finset.sum s fun (a : α) => f a n :=
  sorry

theorem mul_Sup {s : set ennreal} {a : ennreal} :
    a * Sup s = supr fun (i : ennreal) => supr fun (H : i ∈ s) => a * i :=
  sorry

theorem mul_supr {ι : Sort u_1} {f : ι → ennreal} {a : ennreal} :
    a * supr f = supr fun (i : ι) => a * f i :=
  sorry

theorem supr_mul {ι : Sort u_1} {f : ι → ennreal} {a : ennreal} :
    supr f * a = supr fun (i : ι) => f i * a :=
  sorry

protected theorem tendsto_coe_sub {r : nnreal} {b : ennreal} :
    filter.tendsto (fun (b : ennreal) => ↑r - b) (nhds b) (nhds (↑r - b)) :=
  sorry

theorem sub_supr {a : ennreal} {ι : Sort u_1} [hι : Nonempty ι] {b : ι → ennreal} (hr : a < ⊤) :
    (a - supr fun (i : ι) => b i) = infi fun (i : ι) => a - b i :=
  sorry

theorem supr_eq_zero {ι : Sort u_1} {f : ι → ennreal} :
    (supr fun (i : ι) => f i) = 0 ↔ ∀ (i : ι), f i = 0 :=
  sorry

protected theorem has_sum_coe {α : Type u_1} {f : α → nnreal} {r : nnreal} :
    has_sum (fun (a : α) => ↑(f a)) ↑r ↔ has_sum f r :=
  sorry

protected theorem tsum_coe_eq {α : Type u_1} {r : nnreal} {f : α → nnreal} (h : has_sum f r) :
    (tsum fun (a : α) => ↑(f a)) = ↑r :=
  has_sum.tsum_eq (iff.mpr ennreal.has_sum_coe h)

protected theorem coe_tsum {α : Type u_1} {f : α → nnreal} :
    summable f → ↑(tsum f) = tsum fun (a : α) => ↑(f a) :=
  sorry

protected theorem has_sum {α : Type u_1} {f : α → ennreal} :
    has_sum f (supr fun (s : finset α) => finset.sum s fun (a : α) => f a) :=
  tendsto_at_top_supr fun (s t : finset α) => finset.sum_le_sum_of_subset

@[simp] protected theorem summable {α : Type u_1} {f : α → ennreal} : summable f :=
  Exists.intro (supr fun (s : finset α) => finset.sum s fun (a : α) => f a) ennreal.has_sum

theorem tsum_coe_ne_top_iff_summable {β : Type u_2} {f : β → nnreal} :
    (tsum fun (b : β) => ↑(f b)) ≠ ⊤ ↔ summable f :=
  sorry

protected theorem tsum_eq_supr_sum {α : Type u_1} {f : α → ennreal} :
    (tsum fun (a : α) => f a) = supr fun (s : finset α) => finset.sum s fun (a : α) => f a :=
  has_sum.tsum_eq ennreal.has_sum

protected theorem tsum_eq_supr_sum' {α : Type u_1} {f : α → ennreal} {ι : Type u_2}
    (s : ι → finset α) (hs : ∀ (t : finset α), ∃ (i : ι), t ⊆ s i) :
    (tsum fun (a : α) => f a) = supr fun (i : ι) => finset.sum (s i) fun (a : α) => f a :=
  sorry

protected theorem tsum_sigma {α : Type u_1} {β : α → Type u_2} (f : (a : α) → β a → ennreal) :
    (tsum fun (p : sigma fun (a : α) => β a) => f (sigma.fst p) (sigma.snd p)) =
        tsum fun (a : α) => tsum fun (b : β a) => f a b :=
  tsum_sigma' (fun (b : α) => ennreal.summable) ennreal.summable

protected theorem tsum_sigma' {α : Type u_1} {β : α → Type u_2}
    (f : (sigma fun (a : α) => β a) → ennreal) :
    (tsum fun (p : sigma fun (a : α) => β a) => f p) =
        tsum fun (a : α) => tsum fun (b : β a) => f (sigma.mk a b) :=
  tsum_sigma' (fun (b : α) => ennreal.summable) ennreal.summable

protected theorem tsum_prod {α : Type u_1} {β : Type u_2} {f : α → β → ennreal} :
    (tsum fun (p : α × β) => f (prod.fst p) (prod.snd p)) =
        tsum fun (a : α) => tsum fun (b : β) => f a b :=
  tsum_prod' ennreal.summable fun (_x : α) => ennreal.summable

protected theorem tsum_comm {α : Type u_1} {β : Type u_2} {f : α → β → ennreal} :
    (tsum fun (a : α) => tsum fun (b : β) => f a b) =
        tsum fun (b : β) => tsum fun (a : α) => f a b :=
  tsum_comm' ennreal.summable (fun (_x : β) => ennreal.summable) fun (_x : α) => ennreal.summable

protected theorem tsum_add {α : Type u_1} {f : α → ennreal} {g : α → ennreal} :
    (tsum fun (a : α) => f a + g a) = (tsum fun (a : α) => f a) + tsum fun (a : α) => g a :=
  tsum_add ennreal.summable ennreal.summable

protected theorem tsum_le_tsum {α : Type u_1} {f : α → ennreal} {g : α → ennreal}
    (h : ∀ (a : α), f a ≤ g a) : (tsum fun (a : α) => f a) ≤ tsum fun (a : α) => g a :=
  tsum_le_tsum h ennreal.summable ennreal.summable

protected theorem sum_le_tsum {α : Type u_1} {f : α → ennreal} (s : finset α) :
    (finset.sum s fun (x : α) => f x) ≤ tsum fun (x : α) => f x :=
  sum_le_tsum s (fun (x : α) (hx : ¬x ∈ s) => zero_le (f x)) ennreal.summable

protected theorem tsum_eq_supr_nat' {f : ℕ → ennreal} {N : ℕ → ℕ}
    (hN : filter.tendsto N filter.at_top filter.at_top) :
    (tsum fun (i : ℕ) => f i) =
        supr fun (i : ℕ) => finset.sum (finset.range (N i)) fun (i : ℕ) => f i :=
  sorry

protected theorem tsum_eq_supr_nat {f : ℕ → ennreal} :
    (tsum fun (i : ℕ) => f i) =
        supr fun (i : ℕ) => finset.sum (finset.range i) fun (i : ℕ) => f i :=
  ennreal.tsum_eq_supr_sum' (fun (i : ℕ) => finset.range i) finset.exists_nat_subset_range

protected theorem le_tsum {α : Type u_1} {f : α → ennreal} (a : α) :
    f a ≤ tsum fun (a : α) => f a :=
  le_tsum' ennreal.summable a

protected theorem tsum_eq_top_of_eq_top {α : Type u_1} {f : α → ennreal} :
    (∃ (a : α), f a = ⊤) → (tsum fun (a : α) => f a) = ⊤ :=
  fun (ᾰ : ∃ (a : α), f a = ⊤) =>
    Exists.dcases_on ᾰ
      fun (ᾰ_w : α) (ᾰ_h : f ᾰ_w = ⊤) =>
        idRhs ((tsum fun (a : α) => f a) = ⊤) (top_unique (ᾰ_h ▸ ennreal.le_tsum ᾰ_w))

protected theorem ne_top_of_tsum_ne_top {α : Type u_1} {f : α → ennreal}
    (h : (tsum fun (a : α) => f a) ≠ ⊤) (a : α) : f a ≠ ⊤ :=
  fun (ha : f a = ⊤) => h (ennreal.tsum_eq_top_of_eq_top (Exists.intro a ha))

protected theorem tsum_mul_left {α : Type u_1} {a : ennreal} {f : α → ennreal} :
    (tsum fun (i : α) => a * f i) = a * tsum fun (i : α) => f i :=
  sorry

protected theorem tsum_mul_right {α : Type u_1} {a : ennreal} {f : α → ennreal} :
    (tsum fun (i : α) => f i * a) = (tsum fun (i : α) => f i) * a :=
  sorry

@[simp] theorem tsum_supr_eq {α : Type u_1} (a : α) {f : α → ennreal} :
    (tsum fun (b : α) => supr fun (h : a = b) => f b) = f a :=
  sorry

theorem has_sum_iff_tendsto_nat {f : ℕ → ennreal} (r : ennreal) :
    has_sum f r ↔
        filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top
          (nhds r) :=
  sorry

theorem to_nnreal_apply_of_tsum_ne_top {α : Type u_1} {f : α → ennreal}
    (hf : (tsum fun (i : α) => f i) ≠ ⊤) (x : α) : ↑(function.comp ennreal.to_nnreal f x) = f x :=
  coe_to_nnreal (ennreal.ne_top_of_tsum_ne_top hf x)

theorem summable_to_nnreal_of_tsum_ne_top {α : Type u_1} {f : α → ennreal}
    (hf : (tsum fun (i : α) => f i) ≠ ⊤) : summable (ennreal.to_nnreal ∘ f) :=
  sorry

protected theorem tsum_apply {ι : Type u_1} {α : Type u_2} {f : ι → α → ennreal} {x : α} :
    tsum (fun (i : ι) => f i) x = tsum fun (i : ι) => f i x :=
  tsum_apply (iff.mpr pi.summable fun (_x : α) => ennreal.summable)

theorem tsum_sub {f : ℕ → ennreal} {g : ℕ → ennreal} (h₁ : (tsum fun (i : ℕ) => g i) < ⊤)
    (h₂ : g ≤ f) :
    (tsum fun (i : ℕ) => f i - g i) = (tsum fun (i : ℕ) => f i) - tsum fun (i : ℕ) => g i :=
  sorry

end ennreal

namespace nnreal

/-- Comparison test of convergence of `ℝ≥0`-valued series. -/
theorem exists_le_has_sum_of_le {β : Type u_2} {f : β → nnreal} {g : β → nnreal} {r : nnreal}
    (hgf : ∀ (b : β), g b ≤ f b) (hfr : has_sum f r) : ∃ (p : nnreal), ∃ (H : p ≤ r), has_sum g p :=
  sorry

/-- Comparison test of convergence of `ℝ≥0`-valued series. -/
theorem summable_of_le {β : Type u_2} {f : β → nnreal} {g : β → nnreal}
    (hgf : ∀ (b : β), g b ≤ f b) : summable f → summable g :=
  sorry

/-- A series of non-negative real numbers converges to `r` in the sense of `has_sum` if and only if
the sequence of partial sum converges to `r`. -/
theorem has_sum_iff_tendsto_nat {f : ℕ → nnreal} {r : nnreal} :
    has_sum f r ↔
        filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top
          (nhds r) :=
  sorry

theorem not_summable_iff_tendsto_nat_at_top {f : ℕ → nnreal} :
    ¬summable f ↔
        filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top
          filter.at_top :=
  sorry

theorem summable_iff_not_tendsto_nat_at_top {f : ℕ → nnreal} :
    summable f ↔
        ¬filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i)
            filter.at_top filter.at_top :=
  sorry

theorem summable_of_sum_range_le {f : ℕ → nnreal} {c : nnreal}
    (h : ∀ (n : ℕ), (finset.sum (finset.range n) fun (i : ℕ) => f i) ≤ c) : summable f :=
  sorry

theorem tsum_le_of_sum_range_le {f : ℕ → nnreal} {c : nnreal}
    (h : ∀ (n : ℕ), (finset.sum (finset.range n) fun (i : ℕ) => f i) ≤ c) :
    (tsum fun (i : ℕ) => f i) ≤ c :=
  le_of_tendsto' (iff.mp has_sum_iff_tendsto_nat (summable.has_sum (summable_of_sum_range_le h))) h

theorem tsum_comp_le_tsum_of_inj {α : Type u_1} {β : Type u_2} {f : α → nnreal} (hf : summable f)
    {i : β → α} (hi : function.injective i) :
    (tsum fun (x : β) => f (i x)) ≤ tsum fun (x : α) => f x :=
  tsum_le_tsum_of_inj i hi (fun (c : α) (hc : ¬c ∈ set.range i) => zero_le (f c))
    (fun (b : β) => le_refl (f (i b))) (summable_comp_injective hf hi) hf

theorem summable_sigma {α : Type u_1} {β : α → Type u_2} {f : (sigma fun (x : α) => β x) → nnreal} :
    summable f ↔
        (∀ (x : α), summable fun (y : β x) => f (sigma.mk x y)) ∧
          summable fun (x : α) => tsum fun (y : β x) => f (sigma.mk x y) :=
  sorry

/-- For `f : ℕ → ℝ≥0`, then `∑' k, f (k + i)` tends to zero. This does not require a summability
assumption on `f`, as otherwise all sums are zero. -/
theorem tendsto_sum_nat_add (f : ℕ → nnreal) :
    filter.tendsto (fun (i : ℕ) => tsum fun (k : ℕ) => f (k + i)) filter.at_top (nhds 0) :=
  sorry

end nnreal

namespace ennreal

theorem tendsto_sum_nat_add (f : ℕ → ennreal) (hf : (tsum fun (i : ℕ) => f i) ≠ ⊤) :
    filter.tendsto (fun (i : ℕ) => tsum fun (k : ℕ) => f (k + i)) filter.at_top (nhds 0) :=
  sorry

end ennreal

theorem tsum_comp_le_tsum_of_inj {α : Type u_1} {β : Type u_2} {f : α → ℝ} (hf : summable f)
    (hn : ∀ (a : α), 0 ≤ f a) {i : β → α} (hi : function.injective i) : tsum (f ∘ i) ≤ tsum f :=
  sorry

/-- Comparison test of convergence of series of non-negative real numbers. -/
theorem summable_of_nonneg_of_le {β : Type u_2} {f : β → ℝ} {g : β → ℝ} (hg : ∀ (b : β), 0 ≤ g b)
    (hgf : ∀ (b : β), g b ≤ f b) (hf : summable f) : summable g :=
  sorry

/-- A series of non-negative real numbers converges to `r` in the sense of `has_sum` if and only if
the sequence of partial sum converges to `r`. -/
theorem has_sum_iff_tendsto_nat_of_nonneg {f : ℕ → ℝ} (hf : ∀ (i : ℕ), 0 ≤ f i) (r : ℝ) :
    has_sum f r ↔
        filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top
          (nhds r) :=
  sorry

theorem ennreal.of_real_tsum_of_nonneg {α : Type u_1} {f : α → ℝ} (hf_nonneg : ∀ (n : α), 0 ≤ f n)
    (hf : summable f) :
    ennreal.of_real (tsum fun (n : α) => f n) = tsum fun (n : α) => ennreal.of_real (f n) :=
  sorry

theorem not_summable_iff_tendsto_nat_at_top_of_nonneg {f : ℕ → ℝ} (hf : ∀ (n : ℕ), 0 ≤ f n) :
    ¬summable f ↔
        filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top
          filter.at_top :=
  sorry

theorem summable_iff_not_tendsto_nat_at_top_of_nonneg {f : ℕ → ℝ} (hf : ∀ (n : ℕ), 0 ≤ f n) :
    summable f ↔
        ¬filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i)
            filter.at_top filter.at_top :=
  sorry

theorem summable_sigma_of_nonneg {α : Type u_1} {β : α → Type u_2}
    {f : (sigma fun (x : α) => β x) → ℝ} (hf : ∀ (x : sigma fun (x : α) => β x), 0 ≤ f x) :
    summable f ↔
        (∀ (x : α), summable fun (y : β x) => f (sigma.mk x y)) ∧
          summable fun (x : α) => tsum fun (y : β x) => f (sigma.mk x y) :=
  sorry

theorem summable_of_sum_range_le {f : ℕ → ℝ} {c : ℝ} (hf : ∀ (n : ℕ), 0 ≤ f n)
    (h : ∀ (n : ℕ), (finset.sum (finset.range n) fun (i : ℕ) => f i) ≤ c) : summable f :=
  sorry

theorem tsum_le_of_sum_range_le {f : ℕ → ℝ} {c : ℝ} (hf : ∀ (n : ℕ), 0 ≤ f n)
    (h : ∀ (n : ℕ), (finset.sum (finset.range n) fun (i : ℕ) => f i) ≤ c) :
    (tsum fun (i : ℕ) => f i) ≤ c :=
  sorry

/-- In an emetric ball, the distance between points is everywhere finite -/
theorem edist_ne_top_of_mem_ball {β : Type u_2} [emetric_space β] {a : β} {r : ennreal}
    (x : ↥(emetric.ball a r)) (y : ↥(emetric.ball a r)) :
    edist (subtype.val x) (subtype.val y) ≠ ⊤ :=
  sorry

/-- Each ball in an extended metric space gives us a metric space, as the edist
is everywhere finite. -/
def metric_space_emetric_ball {β : Type u_2} [emetric_space β] (a : β) (r : ennreal) :
    metric_space ↥(emetric.ball a r) :=
  emetric_space.to_metric_space edist_ne_top_of_mem_ball

theorem nhds_eq_nhds_emetric_ball {β : Type u_2} [emetric_space β] (a : β) (x : β) (r : ennreal)
    (h : x ∈ emetric.ball a r) : nhds x = filter.map coe (nhds { val := x, property := h }) :=
  Eq.symm (map_nhds_subtype_coe_eq h (mem_nhds_sets emetric.is_open_ball h))

theorem tendsto_iff_edist_tendsto_0 {α : Type u_1} {β : Type u_2} [emetric_space α] {l : filter β}
    {f : β → α} {y : α} :
    filter.tendsto f l (nhds y) ↔ filter.tendsto (fun (x : β) => edist (f x) y) l (nhds 0) :=
  sorry

/-- Yet another metric characterization of Cauchy sequences on integers. This one is often the
most efficient. -/
theorem emetric.cauchy_seq_iff_le_tendsto_0 {α : Type u_1} {β : Type u_2} [emetric_space α]
    [Nonempty β] [semilattice_sup β] {s : β → α} :
    cauchy_seq s ↔
        ∃ (b : β → ennreal),
          (∀ (n m N : β), N ≤ n → N ≤ m → edist (s n) (s m) ≤ b N) ∧
            filter.tendsto b filter.at_top (nhds 0) :=
  sorry

theorem continuous_of_le_add_edist {α : Type u_1} [emetric_space α] {f : α → ennreal} (C : ennreal)
    (hC : C ≠ ⊤) (h : ∀ (x y : α), f x ≤ f y + C * edist x y) : continuous f :=
  sorry

theorem continuous_edist {α : Type u_1} [emetric_space α] :
    continuous fun (p : α × α) => edist (prod.fst p) (prod.snd p) :=
  sorry

theorem continuous.edist {α : Type u_1} {β : Type u_2} [emetric_space α] [topological_space β]
    {f : β → α} {g : β → α} (hf : continuous f) (hg : continuous g) :
    continuous fun (b : β) => edist (f b) (g b) :=
  continuous.comp continuous_edist (continuous.prod_mk hf hg)

theorem filter.tendsto.edist {α : Type u_1} {β : Type u_2} [emetric_space α] {f : β → α} {g : β → α}
    {x : filter β} {a : α} {b : α} (hf : filter.tendsto f x (nhds a))
    (hg : filter.tendsto g x (nhds b)) :
    filter.tendsto (fun (x : β) => edist (f x) (g x)) x (nhds (edist a b)) :=
  filter.tendsto.comp (continuous.tendsto continuous_edist (a, b))
    (filter.tendsto.prod_mk_nhds hf hg)

theorem cauchy_seq_of_edist_le_of_tsum_ne_top {α : Type u_1} [emetric_space α] {f : ℕ → α}
    (d : ℕ → ennreal) (hf : ∀ (n : ℕ), edist (f n) (f (Nat.succ n)) ≤ d n) (hd : tsum d ≠ ⊤) :
    cauchy_seq f :=
  sorry

theorem emetric.is_closed_ball {α : Type u_1} [emetric_space α] {a : α} {r : ennreal} :
    is_closed (emetric.closed_ball a r) :=
  is_closed_le (continuous.edist continuous_id continuous_const) continuous_const

/-- If `edist (f n) (f (n+1))` is bounded above by a function `d : ℕ → ennreal`,
then the distance from `f n` to the limit is bounded by `∑'_{k=n}^∞ d k`. -/
theorem edist_le_tsum_of_edist_le_of_tendsto {α : Type u_1} [emetric_space α] {f : ℕ → α}
    (d : ℕ → ennreal) (hf : ∀ (n : ℕ), edist (f n) (f (Nat.succ n)) ≤ d n) {a : α}
    (ha : filter.tendsto f filter.at_top (nhds a)) (n : ℕ) :
    edist (f n) a ≤ tsum fun (m : ℕ) => d (n + m) :=
  sorry

/-- If `edist (f n) (f (n+1))` is bounded above by a function `d : ℕ → ennreal`,
then the distance from `f 0` to the limit is bounded by `∑'_{k=0}^∞ d k`. -/
theorem edist_le_tsum_of_edist_le_of_tendsto₀ {α : Type u_1} [emetric_space α] {f : ℕ → α}
    (d : ℕ → ennreal) (hf : ∀ (n : ℕ), edist (f n) (f (Nat.succ n)) ≤ d n) {a : α}
    (ha : filter.tendsto f filter.at_top (nhds a)) : edist (f 0) a ≤ tsum fun (m : ℕ) => d m :=
  sorry

end Mathlib