import Mathlib.Algebra.Field.Basic
import Mathlib.Algebra.Hom.Group
import Aesop

import Util.Arithmetic
import RealsQuasiMorphisms.Basic

open scoped Int.natAbs

variable {G : Type _} [AddCommGroup G]

section Comp
namespace AlmostAdditive

/-- The composition of almost additive functions (on appropriate domains) is
almost additive. -/
protected theorem comp
        ⦃f₁ : ℤ → ℤ⦄ ⦃bound₁ : ℕ⦄ (h₁ : AlmostAdditive f₁ bound₁)
        ⦃f₂ : G → ℤ⦄ ⦃bound₂ : ℕ⦄ (h₂ : AlmostAdditive f₂ bound₂)
    : AlmostAdditive (f₁ ∘ f₂) <|
        (bound₁ + |f₁ 1|) * bound₂ + bound₁ * 3 := fun x y =>
  calc |f₁ (f₂ (x + y)) - f₁ (f₂ x) - f₁ (f₂ y)|
    ≤ |f₁ (f₂ (x + y)) - f₁ (f₂ (x + y) - f₂ x - f₂ y) - f₁ (f₂ x + f₂ y)|
      + |f₁ (f₂ (x + y) - f₂ x - f₂ y)|
      + |f₁ (f₂ x + f₂ y) - f₁ (f₂ x) - f₁ (f₂ y)|
        := by lax_exact Int.natAbs_add_le₃ (f₁ (f₂ (x + y)) - f₁ (f₂ (x + y) - f₂ x - f₂ y) - f₁ (f₂ x + f₂ y))
                                           (f₁ (f₂ (x + y) - f₂ x - f₂ y))
                                           (f₁ (f₂ x + f₂ y) - f₁ (f₂ x) - f₁ (f₂ y))
              linarith
  _ ≤ bound₁
      + ((bound₁ + |f₁ 1|) * |f₂ (x + y) - f₂ x - f₂ y| + bound₁)
      + bound₁
        := by conv in f₁ (f₂ (x + y)) =>
                /- Need `f₂ (x + y)` like this to use `h₁.almost_additive`. -/
                rw [show f₂ (x + y) = (f₂ (x + y) - f₂ x - f₂ y) + (f₂ x + f₂ y)
                    by linarith]
              refine Nat.add_le_add₃ ?_ ?using_lemma ?_;
              case using_lemma => apply h₁.linear_growth_upper_bound_int
              all_goals apply h₁.almost_additive
  _ = (bound₁ + |f₁ 1|) * |f₂ (x + y) - f₂ x - f₂ y| + bound₁ * 3 := by linarith
  _ ≤ (bound₁ + |f₁ 1|) * bound₂ + bound₁ * 3
        := h₂.almost_additive .. |> Nat.mul_le_mul_left (k := _)
                                 |> Nat.add_le_add_right (k := _)

/-- If f₂ - f₁ is bounded then f ∘ (f₂ - f₁) is bounded. -/
lemma comp_congr_right
        ⦃f  : ℤ → ℤ⦄ ⦃bound : ℕ⦄ (h : AlmostAdditive f  bound)
        ⦃f₁ f₂ : G → ℤ⦄ ⦃bound' : ℕ⦄ (h' : Bounded (-f₁ + f₂) bound')
    : Bounded (-f.comp f₁ + f.comp f₂) <|
        (bound + |f 1|) * bound' + bound * 2 := fun g =>
  calc |(-f (f₁ g)) + f (f₂ g)|
      = |f (f₁ g + (f₂ g - f₁ g)) - f (f₁ g) - f (f₂ g - f₁ g)
         + f (f₂ g - f₁ g)|
          := congrArg Int.natAbs <| by
               rw [Int.add_sub_cancel_right (f₁ g) (f₂ g),
                   ←Int.sub_eq_neg_add, Int.sub_add_cancel]
    _ ≤ bound + ((bound + |f 1|) * |f₂ g - f₁ g| + bound)
          := Trans.trans (Int.natAbs_add_le ..) <| Nat.add_le_add
               (h.almost_additive ..)
               (h.linear_growth_upper_bound_int ..)
    _ ≤ (bound + |f 1|) * bound' + bound * 2
          := by have : |f₂ g - f₁ g| ≤ bound' := Int.sub_eq_neg_add .. ▸ h' g
                have := Nat.mul_le_mul_left (bound + (f 1).natAbs) this
                linarith [this]

/-- Composition of almost additive functions is distributive over addition on
the right, up to a bounded function. -/
lemma almost_comp_add
        ⦃f  : ℤ → ℤ⦄ ⦃bound : ℕ⦄ (h : AlmostAdditive f  bound)
        (f₁ f₂ : G → ℤ)
    : Bounded (-f ∘ (f₁ + f₂) + (f ∘ f₁ + f ∘ f₂)) bound := fun g => by
  show |(-f (f₁ g + f₂ g)) + (f (f₁ g) + f (f₂ g))| ≤ bound
  rewrite [←Int.natAbs_neg]
  lax_exact h.almost_additive (f₁ g) (f₂ g); linarith

end AlmostAdditive

namespace AlmostHom

/-- Composition of almost-homomorphisms (with appropriate domains), returning
another almost-homomorphism. -/
protected def comp  (f₁ : AlmostHom ℤ) (f₂ : AlmostHom G) : AlmostHom G where
  toFun := f₁ ∘ f₂
  almostAdditive :=
    let ⟨_, h₁⟩ := f₁.almostAdditive
    let ⟨_, h₂⟩ := f₂.almostAdditive
    -- bound is filled in based on the proof :)
    ⟨_, AlmostAdditive.comp h₁ h₂⟩

/-- Concrete statement of well-defined-ness of `QuasiHom.comp` wrt second argument. -/
lemma comp_congr_right (f : AlmostHom ℤ)
        ⦃f₁ f₂ : AlmostHom G⦄ (h : ∃ bound : ℕ, Bounded (-f₁ + f₂) bound)
    : ∃ bound : ℕ, Bounded (-f.comp f₁ + f.comp f₂) bound :=
  let ⟨_, h'⟩ := h; let ⟨_, h⟩ := f.almostAdditive
  ⟨_, h.comp_congr_right h'⟩

/-- Concrete statement of additivity of `QuasiHom.comp` wrt second argument. -/
lemma almost_comp_add (f : AlmostHom ℤ) (f₁ f₂ : AlmostHom G)
    : ∃ bound : ℕ, Bounded (-f.comp (f₁ + f₂) + (f.comp f₁ + f.comp f₂)) bound :=
  let ⟨_, h⟩ := f.almostAdditive
  ⟨_, h.almost_comp_add f₁ f₂⟩

/-- Left distributivity of composition over addition. -/
lemma add_comp (f : AlmostHom G) (f₁ f₂ : AlmostHom ℤ)
    : (f₁ + f₂).comp f = f₁.comp f + f₂.comp f := by ext; rfl
/-- If f₁ is bounded then f₁.comp f₂ is bounded. -/
lemma bounded_comp (f₂ : AlmostHom G)
                   ⦃f₁ : AlmostHom ℤ⦄ (h : ∃ bound : ℕ, Bounded f₁ bound)
    : ∃ bound : ℕ, Bounded (f₁.comp f₂) bound :=
  let ⟨bound, h⟩ := h; ⟨bound, fun g => h (f₂ g)⟩

/-- Composition of AlmostHoms f g is almost equal to (f n * g n)/n -/
private lemma comp_almost_mul (f₁ f₂ : AlmostHom ℤ)
    : ∃ k, ∀ n, |n * (f₁.comp f₂ n) - f₂ n * f₁ n| ≤ (|n| + 1) * k := by
  let ⟨a', b', hlin⟩ := linear_growth_upper_bound_int f₂
  let ⟨b₁, hf₁⟩ := f₁.almostAdditive
  exact ⟨_, by
    intro n
    have hypcomm := AlmostAdditive.almost_smul_comm (hf₁) (f₂ n) n 1
    specialize hlin n
    simp only [smul_eq_mul, mul_one] at hypcomm
    calc |n * (f₁.comp f₂ n) - f₂ n * f₁ n|
        ≤ b₁*(|f₂ n| + |n| + 2) := hypcomm
      _ ≤ b₁*(a'*|n| + b' + |n| + 2) :=
          by apply mul_le_mul_of_nonneg_left
              (by simp only [add_le_add_iff_right, hlin]) (zero_le _)
      _ = b₁*(|n| * (a' + 1) + (b'+ 2)) := by ring
      _ ≤ b₁*(|n| * (a' + 1) + (b'+ 2)) + b₁*(a'+1) :=
          by simp only [le_add_iff_nonneg_right, zero_le]
      _ ≤ b₁*(|n| * (a' + 1) + (b'+ 2)) + b₁*(a'+1) + b₁*(|n|)*(b'+2) :=
          by simp only [le_add_iff_nonneg_right, zero_le]
      _ = (|n|+1)*(b₁*(a'+1 + b'+2)) := by ring
  ⟩

lemma succ_le_two_mul (a : ℕ) (ha : a ≠ 0) : a+1 ≤ 2*a := by cases a; contradiction; apply Nat.succ_le.2; linarith [NeZero.pos]

/-- Composition of AlmostHoms is commutative. -/
lemma comp_almost_comm (f₁ f₂ : AlmostHom ℤ)
    : (f₁.comp f₂) - (f₂.comp f₁) ∈ boundedAlmostHoms ℤ := by
  simp only [boundedAlmostHoms, Bounded, AddSubgroup.mem_mk, Set.mem_setOf_eq]
  let ⟨k₁, hf₁⟩ := comp_almost_mul f₁ f₂
  let ⟨k₂, hf₂⟩ := comp_almost_mul f₂ f₁
  exact ⟨_, by
    intro n
    have triag := Int.natAbs_add_le (n * (f₁.comp f₂ n) - f₂ n * f₁ n) (f₂ n * f₁ n - n * (f₂.comp f₁ n))
    simp only [sub_add_sub_cancel, Int.diff_eq] at triag
    if c: n = 0
    then
      simp only [c, zero_mul, zero_sub, Int.natAbs_neg, ge_iff_le] at hf₁ hf₂ |-
      exact self_le_add_right |(f₁.comp f₂ - f₂.comp f₁) 0| (2*(k₁ + k₂))
    else
    have goal_mul_n :=
      calc
        |n| * |f₁.comp f₂ n - f₂.comp f₁ n|
          = |n*f₁.comp f₂ n - n*f₂.comp f₁ n| := by rw [←Int.natAbs_mul, mul_sub_left_distrib]
        _ ≤ |n*f₁.comp f₂ n - f₂ n * f₁ n| + |f₂ n * f₁ n - n*f₂.comp f₁ n| := triag
        _ ≤ |n*f₁.comp f₂ n - f₂ n * f₁ n| + (|n|+1)*k₂  :=
            by
              rw [mul_comm $ f₂ n, ←Int.natAbs_neg (f₁ n * f₂ n - n*f₂.comp f₁ n)]
              apply Nat.add_le_add_left;
              simp only [neg_sub, Int.diff_eq]; exact hf₂ ..
        _ ≤ (|n|+1)*k₁ + (|n|+1)*k₂ := Nat.add_le_add_right (hf₁ ..) ..
        _ = (|n|+1)*(k₁ + k₂) := by ring
        _ ≤ |n| * (2*(k₁ + k₂)) :=
            by
              rw [←mul_assoc, mul_comm |n|]
              exact Nat.mul_le_mul_of_nonneg_right
                <| succ_le_two_mul |n|
                <| Int.natAbs_ne_zero.2 c
    calc |f₁.comp f₂ n - f₂.comp f₁ n|
      ≤ 2*(k₁ + k₂) := le_of_mul_le_mul_left goal_mul_n
        <| Or.resolve_left (Nat.eq_zero_or_pos ..) (Int.natAbs_ne_zero.2 c)
    _ ≤ _ := self_le_add_left  (2*(k₁ + k₂)) |(f₁.comp f₂ - f₂.comp f₁) 0| ⟩

def non_zero (f : AlmostHom ℤ) : AlmostHom ℤ :=
  ⟨fun n => if f n = 0 then 1 else f n,
    by
      let ⟨f,b,hf⟩ := f
      unfold AlmostAdditive at hf
      exact ⟨2+b,
      by
        rw [AlmostAdditive]
        intro g₁ g₂
        split
        case inl hg =>
          by_cases c:f g₁ = 0
          <;> by_cases d:f g₂ = 0
          <;> specialize hf g₁ g₂
          <;> simp_all only [c, d, not_false_iff, sub_self, Int.natAbs_zero, zero_le, ite_true, ite_false, zero_sub, Int.natAbs_neg, Int.natAbs_one, le_add_iff_nonneg_right]
          · linarith -- TODO remove linarith
          · exact le_add_left hf
          · simp only [sub_sub_cancel_left, sub_zero] at hf |-; exact le_add_left hf
          · rw [Int.sub_eq_add_neg,
                Int.sub_eq_add_neg,
                add_assoc,
                ←Int.sub_eq_add_neg]
            have := Int.natAbs_add_le 1 (-f g₁ - f g₂)
            rw [Int.natAbs_one] at this
            linarith
        case inr hg =>
          by_cases c:f g₁ = 0
          <;> by_cases d:f g₂ = 0
          <;> specialize hf g₁ g₂
          <;> simp_all only [sub_zero, ite_true, ite_false]
          · have := Int.natAbs_add_le₃ (f (g₁ + g₂)) (-1) (-1)
            rw [Int.sub_eq_add_neg, Int.sub_eq_add_neg]
            simp_all only [Int.natAbs_neg, Int.natAbs_one, ge_iff_le]; linarith
          · rw [Int.sub_eq_add_neg,
                Int.sub_eq_add_neg,
                add_assoc, add_comm (-1),
                ←add_assoc,
                ←@Int.sub_eq_add_neg _ (f g₂)]
            have := Int.natAbs_add_le (f (g₁ + g₂) - f g₂) (-1)
            rw [Int.natAbs_neg, Int.natAbs_one] at this
            linarith
          · rw [Int.sub_eq_add_neg]
            have := Int.natAbs_add_le (f (g₁ + g₂) - f g₁) (-1)
            rw [Int.natAbs_neg, Int.natAbs_one] at this
            linarith
          · linarith [hf ..]⟩⟩

end AlmostHom

-- Tidy up the proof and add it to suitable namespace
@[aesop norm unfold] def smulHom : QuasiHom ℤ →+ QuasiHom G →+ QuasiHom G := by
  /- Skeleton. This is glue code tying `Quotient`s and
  `QuotientAddGroup`s and `MonoidHom`-related functions to define the
  homomorphism in terms of the actual concrete proofs needed, which
  are given as holes (except for the function, which is filled in). -/
  open QuotientAddGroup in
  refine
    lift (boundedAlmostHoms ℤ)
      (AddMonoidHom.mk' (fun f => AddMonoidHom.mk'
          (Quotient.map (sa := leftRel _) (sb := leftRel _)
            /- Function definition -/
            f.comp
            /- Well-defined wrt second arg -/
            (fun f₁ f₂ =>
              show (leftRel _).r .. → (leftRel _).r ..
                by (repeat rewrite [leftRel_apply]); exact
              f.comp_congr_right (f₁ := f₁) (f₂ := f₂)))
          /- Hom wrt second arg as `QuasiHom G` -/
          (Quotient.ind₂ <| fun g₁ g₂ => Quotient.sound <|
           show (leftRel _).r (f.comp (g₁ + g₂)) (f.comp g₁ + f.comp g₂)
             by rewrite [leftRel_apply]; exact
           f.almost_comp_add g₁ g₂))
        /- Hom wrt first arg as `AlmostHom ℤ` -/
        fun f₁ f₂ =>
          AddMonoidHom.ext <|
          Quotient.ind <| fun g => congrArg mk <|
          g.add_comp f₁ f₂)
      /- Show output is 0 if first arg is in `boundedAlmostHoms ℤ`
      (i.e, well-defined wrt first arg as `QuasiHom ℤ`) -/
      fun f h =>
        AddMonoidHom.ext <|
        Quotient.ind <| fun g => Quotient.sound <| by
          simp only [HasEquiv.Equiv, leftRel_apply];
          show -f.comp g + 0 ∈ boundedAlmostHoms G
          rewrite [add_zero, neg_mem_iff]; exact g.bounded_comp h

namespace QuasiHom

/- The following 'helper lemmas' are for showing field structure. -/

private lemma right_distrib (a b c : QuasiHom ℤ) :
    smulHom (a + b) c = smulHom a c + smulHom b c := by
  rw [AddMonoidHom.map_add]; apply AddMonoidHom.add_apply

private lemma zero_mul (a : QuasiHom ℤ) : smulHom 0 a = 0 := by
  simp only [map_zero, AddMonoidHom.zero_apply]

private lemma mul_zero (a : QuasiHom ℤ) : @smulHom ℤ _ a 0 = 0 := by
  simp only [map_zero]

private lemma mul_assoc (a b c : QuasiHom ℤ) :
    smulHom (smulHom a b) c = smulHom a (smulHom b c) := by
  apply QuotientAddGroup.induction_on a
  apply QuotientAddGroup.induction_on b
  apply QuotientAddGroup.induction_on c
  intro _ _ _; rfl

private def one : QuasiHom ℤ := ⟦ ⟨ fun n => n, ⟨0, by intros _ _ ; simp only
                      [add_sub_cancel', sub_self,
                      Int.natAbs_zero, le_refl]⟩⟩  ⟧

private def one_mul  (a : QuasiHom ℤ) : smulHom one a = a := by
  apply QuotientAddGroup.induction_on a; intro _; rfl

private def mul_one (a : QuasiHom ℤ) : smulHom a one = a := by
  apply QuotientAddGroup.induction_on a; intro _; rfl

private def inv (a : QuasiHom ℤ) : QuasiHom ℤ := by
  sorry

private def exists_pair_ne : one ≠ ⟦⟨0, 0, fun _ _ => Nat.le_refl ..⟩⟧ := by
  /- rewrite [show ∀ a : QuasiHom ℤ, a ≠ 0 ↔ ¬a = 0 by intro; rfl] -/
  /- by_contra h -/
  /- apply QuotientAddGroup.eq -/

  /- apply Quotient.exact  (⟨ fun n => n, ⟨0, by intros _ _ ; simp only -/
  /-                         [add_sub_cancel', sub_self, -/
  /-                         Int.natAbs_zero, le_refl]⟩⟩) -/
  /- have := Quotient.exact h; -/
  /- simp [funext] at this; -/
  sorry

private def mul_comm (a b : QuasiHom ℤ) : smulHom a b = smulHom b a := by
  apply QuotientAddGroup.induction_on a
  apply QuotientAddGroup.induction_on b
  intro a b
  rw [smulHom]
  apply (QuotientAddGroup.eq ..).2
  rw [add_comm]
  show a.comp b - b.comp a ∈ boundedAlmostHoms ℤ
  exact AlmostHom.comp_almost_comm a b

/- For some reason LSP is quite slow if it is allowed to work on this instance declaration. -/
#exit
instance : Field (QuasiHom ℤ) :=
  let mul : Mul (QuasiHom ℤ) := ⟨ fun f g => smulHom f g ⟩
  {
    sub_eq_add_neg := SubNegMonoid.sub_eq_add_neg
    left_distrib := by intros _ _ _;  apply AddMonoidHom.map_add
    right_distrib := right_distrib
      -- aesop? (add norm unfold [HMul.hMul, Mul.mul], norm simp AddMonoidHom.map_add, safe apply AddMonoidHom.add_apply)
    mul_comm := mul_comm
    zero_mul  := zero_mul
    mul_zero  := mul_zero
    mul_assoc := mul_assoc
    one :=  one
    one_mul := one_mul
    mul_one := mul_one
    add_left_neg := add_left_neg
    inv := sorry
    exists_pair_ne := sorry
    mul_inv_cancel := sorry
    inv_zero := sorry
  }

end QuasiHom

end Comp


import Project.Main
import Project.Merge
import Project.findMin

universe u

namespace project

variable {le : α → α → Bool}
(not_le_le : ∀ x y, ¬ le x y → le y x)
(le_trans : ∀ x y z, le x y → le y z → le x z)
(le_rfl : ∀ z, le z z)

theorem deleteMin_empty_IsHeap :  deleteMin le xs = none → isEmpty xs := by
intro heq
unfold deleteMin at heq
cases xs with | heap ts =>
cases ts with
| nil =>
  rfl
| cons _ ts =>
  dsimp at heq
  cases ts <;> contradiction

theorem IsHeapForest'_of_IsRankedTree (h₁ : r ≠ 0) :
    IsRankedTree r s nodes → IsHeapForest' (r - 1) nodes
  | .nil => by
    constructor
  | .cons (t := t) (ts := ts) eq rt _ => by
    constructor
    . rw[←eq]
      apply Nat.pred_lt
      simp
      rw[eq]
      assumption
    . assumption
    . have : t.rank = (r + 1) - 1 := by
        simp_all
      rw[this]
      apply IsHeapForest'_of_IsRankedTree
      . simp
      . assumption

theorem children_IsHeap : IsHeap (.heap [h]) → IsHeap h.children := by
  intro h₁
  cases h with | mk val rank children =>
  dsimp at *
  cases h₁ with | cons lt bt h₁ =>
  dsimp at *
  cases children with | heap nodes =>
  dsimp at *
  cases bt with | mk rt =>
  dsimp at rt
  apply IsHeapForest'_of_IsRankedTree (r := 1)
  . simp
  . assumption

theorem IsMinHeap_of_IsMinTree : IsMinTree le val nodes → IsMinHeap le (.heap nodes)
  | .nil => by
    constructor
  | .cons _ _ _=> by
    constructor
    . assumption
    . apply IsMinHeap_of_IsMinTree
      . assumption

theorem children_IsMinHeap : IsMinHeap le (.heap [h]) → IsMinHeap le h.children := by
  intro imh
  cases h with | mk val rank children =>
  dsimp at *
  cases imh with | cons st imh =>
  cases children with | heap nodes =>
  cases imh with | nil =>
  cases st with | mk mt =>
  dsimp at mt
  apply IsMinHeap_of_IsMinTree
  repeat assumption

theorem IsHeapForest'_eraseIdx {id : ℕ} : IsHeapForest' r a → IsHeapForest' r (List.eraseIdx a id)
  | .nil => by
    simp
    constructor
  | .cons (t := t) (ts := ts) lt bt h₁ => by
    unfold List.eraseIdx
    split
    . constructor
    . simp_all
      apply IsHeapForest'_weaken
      . assumption
      . apply le_of_lt
        assumption
    . rename_i eq
      have hf : IsHeapForest' r (t ::ts) := by
        apply IsHeapForest'_of_IsHeapForest' <;> assumption
      constructor
      . simp_all
      . simp_all
      . rw[eq] at hf
        cases hf
        apply IsHeapForest'_eraseIdx
        assumption

theorem IsHeap_delete_BinTree {id : Nat} : IsHeap (.heap (a :: b)) → IsHeap (.heap (a :: List.eraseIdx b id)) := by
  intros h₁
  cases h₁
  constructor
  . assumption
  . assumption
  . apply IsHeapForest'_eraseIdx
    assumption

theorem IsMinHeap_eraseIdx {id : Nat} : IsMinHeap le (.heap b) → IsMinHeap le (.heap (List.eraseIdx b id))
  | .nil => by
    simp
    constructor
  | .cons (n := n) (ns := ns) st mh => by
    unfold List.eraseIdx
    split
    . constructor
    . rename_i heq
      rw[← List.tail_eq_of_cons_eq heq]
      assumption
    . rename_i heq
      constructor
      . rw[← List.head_eq_of_cons_eq heq]
        assumption
      . apply IsMinHeap_eraseIdx
        rw[← List.tail_eq_of_cons_eq heq]
        assumption

theorem IsMinHeap_delete_BinTree {id : Nat} : IsMinHeap le (.heap (a :: b)) → IsMinHeap le (.heap (a :: List.eraseIdx b id)) := by
  intros imh
  cases imh
  constructor
  . assumption
  . apply IsMinHeap_eraseIdx
    assumption

theorem rank_zero_IsRankedTree : IsRankedTree (n + 1) 0 ts → False
  | .cons (ts := ts) _ rt _ => by
    cases rt with
    | cons =>
      apply rank_zero_IsRankedTree
      assumption

theorem min_rank_IsBinTree : IsBinTree t → 0 < t.rank := by
  intro bt
  cases t with | mk val r children =>
  cases r
  . cases bt with | mk rt =>
    simp at rt
    generalize eq : (children.nodes) = ts
    rw[eq] at rt
    cases rt
    . simp
      apply rank_zero_IsRankedTree
      assumption
  . simp_arith

theorem deleteMin_non_empty_minimum : deleteMin le (.heap xs) = some (y, ys) → ∀ x ∈ xs, le y x.val := by
  intros heq t hel
  unfold deleteMin at heq
  split at heq
  . contradiction
  . simp_all
  . simp_all
    dsimp at heq
    rw[← heq.left]
    cases hel
    . rename_i heq₂
      rw[← heq₂]
      apply findMin_is_minimum_head <;> simp_all; assumption
    . apply findMin_is_minimum_tail
      . simp_all
      repeat assumption
      . apply findMin_is_minimum_head
        . simp_all
        repeat assumption
      . assumption

theorem deleteMin_non_empty (h₁ : IsHeap xs) (h₂ : IsMinHeap le xs) : deleteMin le xs = some (y, ys) → IsHeap ys ∧ IsMinHeap le ys :=
match xs with
| .heap [] => by
  intro eq
  unfold deleteMin at eq
  dsimp at eq
  contradiction
| .heap [h] => by
  intro eq
  unfold deleteMin at eq
  dsimp at eq
  rw[Option.some_inj, Prod.eq_iff_fst_eq_snd_eq] at eq
  simp at eq
  apply And.intro
  . rw[← eq.right]
    apply children_IsHeap
    assumption
  . rw[← eq.right]
    apply children_IsMinHeap
    assumption
| .heap (h::hs) => by
  intro dmeq
  unfold deleteMin at dmeq
  dsimp at dmeq
  split at dmeq
  . contradiction
  . rename_i heq
    rw[Option.some_inj] at dmeq
    rw[Prod.eq_iff_fst_eq_snd_eq] at dmeq
    simp at *
    rw[←dmeq.right]
    rw[←heq.left]
    apply And.intro
    . apply children_IsHeap
      rw[heq.right] at h₁
      assumption
    . apply children_IsMinHeap
      rw[heq.right] at h₂
      assumption
  . rename_i xs₂ t₁ ts hne₂ heqh
    rw[Option.some_inj] at dmeq
    unfold findMin at dmeq
    split at dmeq
    . contradiction
    . split at dmeq
      . simp at *
        rename_i ts n btxn bt ts₂ bt₂ n₂ lebt eqeq
        rw[←dmeq.right]
        apply And.intro
        . unfold merge
          split
          rename_i ys₂ xs₃ ts₃ ts₄ heq heq₂
          simp at heq
          unfold List.eraseIdx at heq
          have ihts₃ : IsHeap (.heap ts₃) := by
            split at heq
            . simp_all
            . simp_all
              unfold IsHeap
              simp
              unfold IsHeapForest
              cases h₁
              apply IsHeapForest'_weaken
              . assumption
              . simp_arith
            . rename_i heq₃ _
              rw[← heq]
              apply IsHeap_delete_BinTree
              rw[← heq₃, ← heqh.left, ← heqh.right]
              assumption
          have ihts₄ : IsHeap (.heap ts₄) := by
            clear heq
            rw [← heq₂]
            have (And.intro ml mr) := eqeq
            rw [← ml, ← mr] at *; clear eqeq ml mr
            apply children_IsHeap
            constructor
            . have fm : IsBinTree (findMin le ts₂ (1 + 1) (t₁, 0)).fst := by
                apply IsBinTree_findMin
                . cases h₁ with | cons _ _ hf =>
                  rw[heqh.right] at hf
                  cases hf
                  assumption
                . cases h₁
                  simp_all
              apply min_rank_IsBinTree
              assumption
            . apply IsBinTree_findMin
              . rw[heqh.right] at h₁
                cases h₁ with | cons _ _ hf =>
                  cases hf
                  assumption
              . unfold IsHeap at h₁
                unfold IsHeapForest at h₁
                cases h₁ with | cons _ ht _ =>
                rw[heqh.left] at ht
                assumption
            . constructor
          unfold IsHeap at *
          unfold IsHeapForest at *
          simp at *
          have min_zero : 0 = min 0 0 := by
            simp
          rw[min_zero]
          apply IsHeap_merge <;> assumption
        . unfold merge
          split
          rename_i ys₂ xs₃ ts₃ ts₄ heq heq₂
          simp at heq
          unfold List.eraseIdx at heq
          have h₁₂ : IsMinHeap le (.heap ts₃) := by
            split at heq
            . rw[← heq]
              constructor
            . rename_i ts₅ heq₃ _
              rw[← heqh.right] at heq₃
              have eq : hs = ts₅ := by
                apply List.tail_eq_of_cons_eq
                assumption
              rw[← heq, ← eq]
              cases h₂
              assumption
            . rename_i heq₃ _
              rw[← heq]
              apply IsMinHeap_delete_BinTree
              rw[← heq₃, ← heqh.left, ← heqh.right]
              assumption
          have h₁₃ : IsMinHeap le (.heap ts₄) := by
            rw[← heq₂]
            apply children_IsMinHeap
            constructor
            . apply IsSearchTree_findMin
              . rw[heqh.right] at h₂
                cases h₂ with | cons _ mh =>
                cases mh
                assumption
              . rw[← eqeq.left, ← heqh.left]
                cases h₂
                assumption
            . constructor
          apply IsMinHeap_merge
          simp
          repeat assumption
      . simp at dmeq
        have (And.intro _ heq) := dmeq
        clear dmeq
        unfold merge at heq
        split at heq
        rename_i bt ts _ _ _ _ _ _ _ ts₂ ts₃ heq₃ heq₄
        rw[← heq]
        apply And.intro
        . have ihts₂ : IsHeap (.heap ts₂) := by
            rw[← heq₃]
            unfold IsHeap
            unfold IsHeapForest
            apply IsHeapForest'_eraseIdx
            simp at heqh
            constructor
            . apply min_rank_IsBinTree
              rw[← heqh.left]
              cases h₁
              assumption
            . rw[← heqh.left]
              cases h₁
              assumption
            . cases h₁
              rw[← heqh.left, ← heqh.right]
              assumption
          have h₂₄ : IsHeap (.heap ts₃) := by
            rw[← heq₄]
            apply children_IsHeap
            constructor
            . have ibt : IsBinTree (findMin le ts (1 + 1) (bt, 1)).fst := by
                simp at heqh
                rw[heqh.right] at h₁
                apply IsBinTree_findMin <;>
                  cases h₁ with | cons _ _ hf =>
                  cases hf
                  assumption
              apply min_rank_IsBinTree ibt
            . simp at heqh
              rw[heqh.right] at h₁
              apply IsBinTree_findMin <;>
                cases h₁ with | cons _ _ hf =>
                cases hf
                assumption
            . constructor
          unfold IsHeap at *
          unfold IsHeapForest at *
          simp at *
          have min_zero : 0 = min 0 0 := by
            simp
          rw[min_zero]
          apply IsHeap_merge <;> assumption
        . simp at heqh
          have imhts₂ : IsMinHeap le (.heap ts₂) := by
            rw[← heq₃]
            apply IsMinHeap_eraseIdx
            rw[← heqh.left, ← heqh.right]
            assumption
          have imhts₃ : IsMinHeap le (.heap ts₃) := by
            rw[← heq₄]
            apply children_IsMinHeap
            constructor
            . apply IsSearchTree_findMin <;>
              . rw[heqh.right] at h₂
                cases h₂ with | cons _ mh =>
                cases mh
                assumption
            . constructor
          apply IsMinHeap_merge <;> assumption


import category_theory.limits.shapes.biproducts
import category_theory.preadditive.basic
import category_theory.preadditive.biproducts
import data.fintype.sigma

/-!
Let's show that every preadditive category embeds into a preadditive category with biproducts,
and identify a good universal property.

This is a more advanced exercise, for which I've indicated a suggested structure,
but not written a full solution. I hope this structure will work out!
-/

noncomputable theory

universes v u

variables (C : Type u)

structure additive_envelope :=
(ι : Type)
[fintype : fintype ι]
[decidable_eq : decidable_eq ι]
(val : ι → C)

attribute [instance] additive_envelope.fintype additive_envelope.decidable_eq

variables {C}

def dmatrix {X Y : additive_envelope C} (Z : X.ι → Y.ι → Type*) := Π (i : X.ι) (j : Y.ι), Z i j
-- You may need to develop some API for `dmatrix`, parallel to that in `data.matrix.basic`.
-- One thing you'll certainly need is an "extensionality" lemma,
-- showing that you can prove two `dmatrix`s are equal by checking componentwise.

open category_theory

variables [category C] [preadditive C]

namespace family

def hom (X Y : additive_envelope C) := dmatrix (λ i j, X.val i ⟶ Y.val j)

open_locale big_operators

instance : category (additive_envelope C) :=
{ hom := hom,
  id := λ X i j, if h : i = j then eq_to_hom (by subst h) else 0,
  comp := λ X Y Z f g i k, ∑ (j : Y.ι), f i j ≫ g j k,
  id_comp' := sorry,
  comp_id' := sorry,
  assoc' := sorry, }

variables (C)

@[simps]
def embedding : C ⥤ additive_envelope C :=
{ obj := λ X, ⟨unit, λ _, X⟩,
  map := λ X Y f _ _, f,
  map_id' := sorry,
  map_comp' := sorry, }

lemma embedding.faithful : faithful (embedding C) :=
sorry

instance : preadditive (additive_envelope C) :=
sorry -- probably best to go back and make `dmatrix` an `add_comm_group` first.

open category_theory.limits

instance : has_finite_biproducts (additive_envelope C) :=
{ out := λ n,
  { has_biproduct := λ F, has_biproduct.mk
    { bicone :=
      { X :=
        { ι := Σ (j : fin n), (F j).ι,
          val := λ p, (F p.1).val p.2 },
        ι := sorry,
        π := sorry,
        ι_π := sorry, },
      is_bilimit := sorry }}}

variables {C}

def factor {D : Type u} [category.{v} D] [preadditive D] [has_finite_biproducts D]
  (F : C ⥤ D) : additive_envelope C ⥤ D :=
{ obj := λ X, ⨁ (λ i, F.obj (X.val i)),
  map := sorry,
  map_id' := sorry,
  map_comp' := sorry, }

def factor_factorisation {D : Type u} [category D] [preadditive D] [has_finite_biproducts D]
  (F : C ⥤ D) : F ≅ embedding C ⋙ factor F :=
sorry

end family


import contourintegral
import analysis.calculus.dslope
import analysis.complex.cauchy_integral
noncomputable theory

section tactic

variables {E : Type} {𝕜: Type} [nontrivially_normed_field 𝕜]
[normed_add_comm_group E] [normed_space 𝕜 E] [complete_space E]

/-! Part O. tactic

- # Tactic
-/

@[simp] lemma addminus{a:E}{b:E}: a+(-b)=a - b:=
by {have t: -b=0-b:= by simp, rw t,
    rw ← add_sub_assoc, simp,}

lemma zero_exact{a b:E}(h:0=a-b):a=b:=
begin
  have b_z: b=b+0:= by simp,
  rw h at b_z,
  rw b_z, simp,
end

lemma zero_symm_exact{a b:E}(h:a-b=0):a=b:=
begin
  have h':0=a-b:=by rw h,
  exact zero_exact h',
end

lemma neg_rewrite {a b:E}: -a=b ↔ a=-b :=
by {split, intro f, rw← f,simp,
    intro f, rw f,simp,}

end tactic

variables {E : Type}
[normed_add_comm_group E] [normed_space ℂ E] [complete_space E]

/-! Part O'. basic function

- # Basic Function
-/
/-- The derivative of the inclusion ℝ → ℂ is 1. --/
lemma coe_has_deriv (x:ℝ ):
has_deriv_at (λ (t : ℝ), (t : ℂ)) 1 x :=
begin
  rw has_deriv_at,
  rw has_deriv_at_filter_iff_is_o,
  simp,
end

lemma coe_differentiable:
differentiable ℝ (λ (t : ℝ), (t : ℂ)):=
begin
  intro x,
  exact has_deriv_at.differentiable_at
    (coe_has_deriv x),
end

lemma deriv_of_coe: deriv (λ (x : ℝ), (x : ℂ)) = 1 :=
 deriv_eq coe_has_deriv

lemma complex_affine_has_deriv (a b x:ℂ):
has_deriv_at (λ (z : ℂ), a * z + b) a x :=
begin
  apply has_deriv_at.add_const _ b,
  have frw:(λ (x : ℂ), a * x)=(λ (x : ℂ), x * a):=
    by {ext1,rw mul_comm,}, rw frw,
  exact has_deriv_at_mul_const a,
end

lemma complex_affine_differentiable(a b:ℂ):
differentiable ℂ (λ (z : ℂ), a * z + b):=
begin
  intro x,
  exact has_deriv_at.differentiable_at
    (complex_affine_has_deriv a b x),
end

lemma affine_rtc_has_deriv(a b :ℂ)(x:ℝ):
has_deriv_at ((λ x:ℝ, a*x+b):ℝ→ ℂ) a x:=
begin
  have func_eq:((λ x:ℝ, a*x+b):ℝ→ ℂ)=
    ((λ z:ℂ, a*z+b):ℂ→ ℂ) ∘ (λ (t : ℝ), (t : ℂ)) :=
    by {ext1, simp,},
  have conc: has_deriv_at ((λ (z : ℂ), a * z + b)
    ∘ λ (t : ℝ), ↑t) a x ↔ has_deriv_at ((λ (z : ℂ),
    a * z + b) ∘ λ (t : ℝ), ↑t) (a*1) x:= by simp,
  rw [func_eq, conc],
  apply has_deriv_at.comp,
  exact complex_affine_has_deriv a b _,
  exact coe_has_deriv _,
end

lemma affine_rtc_differentiable(a b:ℂ):
differentiable ℝ ((λ x:ℝ, a*x+b):ℝ→ ℂ):=
begin
  intro x,
  exact has_deriv_at.differentiable_at
    (affine_rtc_has_deriv a b x),
end

lemma affine_rtc_continuous (a b:ℂ):
continuous ((λ x:ℝ, a*x+b):ℝ→ ℂ):=
differentiable.continuous (affine_rtc_differentiable a b)

lemma complex_affine_inverse_has_deriv{a b x:ℂ}
(h: a*x+b≠ 0):
has_deriv_at ((λ(t:ℂ), (a*t + b)⁻¹):ℂ→ ℂ)
(-a/(a*x+b)^2) x :=
has_deriv_at.inv (complex_affine_has_deriv a b x) h

lemma affine_rtc_inverse_has_deriv{a b :ℂ}{x:ℝ}
(h: a*x+b≠ 0):
has_deriv_at ((λ(t:ℝ), (a*t + b)⁻¹):ℝ→ ℂ)
(-a/(a*x+b)^2) x :=
begin
  have func_rw:((λ(t:ℝ), (a*t + b)⁻¹):ℝ→ ℂ)=
    ((λ(t:ℂ), (a*t + b)⁻¹):ℂ→ ℂ) ∘ (λ (t : ℝ), (t : ℂ)):=
    by {ext1, simp,},
  have q:(-a/(a*x+b)^2) =(-a/(a*x+b)^2)*1:= by ring_nf,
  rw [func_rw,q],
  apply has_deriv_at.comp,
  exact complex_affine_inverse_has_deriv h,
  exact coe_has_deriv _,
end

lemma affine_rtc_differentiable_on{a b :ℂ}{s: set ℝ}
(h: ∀(x:ℝ), x∈ s → a*x+b≠ 0):
differentiable_on ℝ ((λ(t:ℝ), (a*t + b)⁻¹):ℝ→ ℂ) s :=
begin
  intros x x_in,
  apply differentiable_at.differentiable_within_at,
  have x_in':= h x x_in,
  exact has_deriv_at.differentiable_at
    (affine_rtc_inverse_has_deriv x_in'),
end

lemma affine_rtc_continuous_on{a b :ℂ}{s: set ℝ}
(h: ∀(x:ℝ), x∈ s → a*x+b≠ 0):
continuous_on ((λ(t:ℝ), (a*t + b)⁻¹):ℝ→ ℂ) s :=
differentiable_on.continuous_on
  (affine_rtc_differentiable_on h)

lemma reciprocal_differentiable_on (c:ℂ):
differentiable_on ℂ (λ (z : ℂ), (z - c)⁻¹) {c}ᶜ :=
begin
  apply differentiable_on.inv,
  apply differentiable.differentiable_on,
  simp,
  intros x x_in, simp at x_in, symmetry,
  intro f, have h:=zero_exact f,
  exact x_in h,
end

lemma reciprocal_continuous_on (c:ℂ) :
continuous_on (λ (z : ℂ), (z - c)⁻¹) {c}ᶜ :=
differentiable_on.continuous_on
  (reciprocal_differentiable_on c)

/-! Part I. Define line segments

- # Line Segments
-/

def line_segment (a:ℂ) (b:ℂ) : ℝ → ℂ :=
  λ (θ : ℝ) , (b-a) * θ + a

/-- The line sgement and circle are both continuously differentiable. --/
lemma deriv_of_line (a:ℂ)(b:ℂ):
  deriv (line_segment a b)  = constant_path (b-a):=
begin
  unfold line_segment constant_path,
  ext1,
  simp,
  rw deriv_of_coe,
  simp,
end

lemma deriv_of_line' (a:ℂ)(b:ℂ)(x : ℝ):
  has_deriv_at (line_segment a b) (constant_path (b-a) x) x:=
begin
  unfold line_segment,
  have h0 : b - a = (b - a) + 0 := by simp,
  rw h0,
  apply has_deriv_at.add,
  have h2 : b - a = (1 : ℂ) • (b - a : ℂ) := by simp,
  rw h2,
  have h1 : (λ (x : ℝ), ((1 : ℂ) • (b - a) + 0) * ↑x) = (λ (x : ℂ), (b - a + 0) * x) ∘ (λ (x : ℝ), (x : ℂ)) := by simp,
  rw h1,
  apply has_deriv_at.scomp,
  {
    rw has_deriv_at,
    rw has_deriv_at_filter_iff_is_o,
    simp,
    have mid: (λ (x' : ℂ), (b - a) * x' -
      (b - a) * ↑x - (x' - ↑x) * (b - a)) =
      (λ(x:ℂ), (0:ℂ)) := by ring_nf,
    rw mid,
    rw asymptotics.is_o_const_left,
    left,
    exact rfl,
  },
  {
    simp,
    exact coe_has_deriv x,
  },
  {
    exact has_deriv_at_const x a,
  }
end

lemma line_is_differentiable (a:ℂ)(b:ℂ):
  differentiable ℝ (line_segment a b):=
begin
  unfold differentiable,
  intro x,
  apply has_deriv_at.differentiable_at (deriv_of_line' a b x),
end

lemma line_is_continuous (a:ℂ )(b:ℂ ):
  continuous (line_segment a b):=
  by {exact differentiable.continuous (line_is_differentiable a b),}

lemma line_is_continuous_on (a:ℂ )(b:ℂ ):
  continuous_on (line_segment a b) (set.interval 0 1):=
  (line_is_continuous a b).continuous_on

lemma line_is_in_C1 (a:ℂ )(b:ℂ):
  continuous (deriv (line_segment a b)):=
begin
  rw deriv_of_line a b,
  exact continuity_of_constant_path (b-a),
end

lemma deriv_line_integrable (a:ℂ)(b:ℂ):
  interval_integrable (deriv (line_segment a b))
  measure_theory.measure_space.volume 0 1:=
continuous.interval_integrable (line_is_in_C1 a b) 0 1

lemma line_integral_ML_inequality{f:ℂ → E}{a b:ℂ}{M:ℝ}
(hf: ∀ z:ℂ, z ∈ set.image (line_segment a b)
(set.interval 0 1) → ∥f z∥ ≤ M):
∥contour_integral f (line_segment a b)∥ ≤
M * complex.abs(b-a) :=
begin
  apply contour_integral_ML_inequality,
  {
    intros z z_in,
    exact hf z ((set.image_subset (line_segment a b)
      set.Ioc_subset_Icc_self) z_in),
  },
  {
    intros x _,
    rw deriv_of_line,
    unfold constant_path,
  },
end

/-! Part II. Define rectangles

- # Rectangles
-/

def rec_bottom (l:ℝ)(b:ℝ)(r:ℝ):=
  line_segment (l+b*complex.I) (r+b*complex.I)
def rec_right (b:ℝ)(r:ℝ)(t:ℝ):=
  line_segment (r+b*complex.I) (r+t*complex.I)
def rec_top (r:ℝ)(t:ℝ)(l:ℝ):=
  line_segment (r+t*complex.I) (l+t*complex.I)
def rec_left (t:ℝ)(l:ℝ)(b:ℝ):=
  line_segment (l+t*complex.I) (l+b*complex.I)

@[protected] lemma bottom_join_right (b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ) :
  rec_bottom l b r 1 = rec_right b r t 0:=
begin
  rw rec_bottom, rw rec_right, repeat {rw line_segment,},
  simp, ring_nf,
end

def rec_bottomright (b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ) :=
  path_concatenation (bottom_join_right b r t l)

@[protected] lemma top_join_left (b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ) :
  rec_top r t l 1 = rec_left t l b 0:=
begin
  rw rec_top, rw rec_left, repeat {rw line_segment,},
  simp, ring_nf,
end

def rec_topleft (b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ) :=
  path_concatenation (top_join_left b r t l)

@[protected] lemma br_join_tl (b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ) :
  rec_bottomright b r t l 1 = rec_topleft b r t l 0 :=
begin
  rw [rec_bottomright, rec_topleft],
  rw path_concatenation_endpoint _,
  rw rec_right, rw path_concatenation, simp,
  rw rec_top, repeat {rw line_segment,}, simp, ring_nf,
end

def rectangle (b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ) :=
  path_concatenation (br_join_tl b r t l)

lemma center_in_interior_rectangle{c:ℂ}
{b r t l:ℝ}(bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r):
c ∈ (set.Ioo l r ×ℂ set.Ioo b t) :=
begin
  unfold set.re_prod_im,
  simp, split, split,
  exact lc, exact cr, split,
  exact bc, exact ct,
end

lemma center_in_interior_rectangle_iff {c:ℂ}
{b r t l:ℝ}: c ∈ (set.Ioo l r ×ℂ set.Ioo b t) ↔
((l < c.re ∧ c.re < r)∧ b < c.im ∧ c.im < t ) :=
begin
  split,
  intros c_in,
  unfold set.re_prod_im at c_in,
  simp at c_in, exact c_in,
  intros in_cond,
  exact center_in_interior_rectangle (in_cond.2.1)
    (in_cond.2.2) (in_cond.1.1) (in_cond.1.2),
end

lemma point_in_closure_rectangle{c:ℂ}
{b r t l:ℝ}(bc: b ≤ c.im) (ct: c.im ≤ t)
(lc: l ≤ c.re) (cr: c.re ≤ r):
c ∈ (set.interval l r ×ℂ set.interval b t) :=
begin
  have bt:b≤ t:= le_trans bc ct,
  have lr:l≤ r:= le_trans lc cr,
  unfold set.re_prod_im,
  simp, split, split,
  rw min_eq_left lr, exact lc,
  rw max_eq_right lr, exact cr,
  split, rw min_eq_left bt, exact bc,
  rw max_eq_right bt, exact ct,
end

lemma interior_rectangle_open(b r t l:ℝ):
is_open (set.Ioo l r ×ℂ set.Ioo b t) :=
is_open.re_prod_im is_open_Ioo is_open_Ioo

lemma interior_rectangle_sub_closure(b r t l:ℝ):
(set.Ioo l r ×ℂ set.Ioo b t)⊆
(set.interval l r ×ℂ set.interval b t) :=
begin
  unfold set.re_prod_im,
  have lr : set.Ioo l r ⊆ set.interval l r :=
    Ioo_subset_interval,
  have bt : set.Ioo b t ⊆ set.interval b t :=
    Ioo_subset_interval,
  intro, simp,
  intros ll rr bb tt, split,
  have x_re_in:x.re∈ set.Ioo l r :=
    by {unfold set.Ioo, simp, split, exact ll, exact rr,},
  exact lr x_re_in,
  have x_im_in:x.im∈ set.Ioo b t:=
    by {unfold set.Ioo, simp, split, exact bb, exact tt,},
  exact bt x_im_in,
end

lemma interior_rectangle_neighborhood {c: ℂ}
{b r t l:ℝ} (hin: c ∈ (set.Ioo l r ×ℂ set.Ioo b t)):
(set.Ioo l r ×ℂ set.Ioo b t) ∈ (nhds c) :=
begin
  rw mem_nhds_iff,
  use (set.Ioo l r ×ℂ set.Ioo b t),
  split, exact rfl.subset,
  split, exact interior_rectangle_open b r t l,
  exact hin,
end

lemma interior_rectangle_neighborhood' {c: ℂ}
{b r t l:ℝ}(bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r):
(set.Ioo l r ×ℂ set.Ioo b t) ∈ (nhds c) :=
  interior_rectangle_neighborhood
  (center_in_interior_rectangle bc ct lc cr)

lemma closure_rectangle_neighborhood {c: ℂ}
{b r t l:ℝ} (hin: c ∈ (set.Ioo l r ×ℂ set.Ioo b t) ):
(set.interval l r ×ℂ set.interval b t) ∈ (nhds c) :=
begin
  rw mem_nhds_iff,
  use (set.Ioo l r ×ℂ set.Ioo b t),
  split, exact interior_rectangle_sub_closure b r t l,
  split, exact interior_rectangle_open b r t l,
  exact hin,
end

lemma closure_rectangle_neighborhood' {c: ℂ}
{b r t l:ℝ}(bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r):
(set.interval l r ×ℂ set.interval b t) ∈ (nhds c) :=
  closure_rectangle_neighborhood
  (center_in_interior_rectangle bc ct lc cr)

lemma image_rec_bottom {l:ℝ}(b:ℝ){r:ℝ}(lr:l≤ r):
set.image (rec_bottom l b r) (set.interval 0 1)
⊆  {z:ℂ | l≤ z.re ∧ z.re≤ r ∧ z.im = b} :=
begin
  unfold rec_bottom, unfold line_segment,
  simp, unfold set.Icc, simp,
  intros a a_ge a_le, split,
  {
    apply mul_nonneg,
    simp, exact lr, exact a_ge,
  },
  {
    have temp: (r-l)*a≤ r-l :=
      by {apply mul_nonneg_le_one_le,
      simp, exact lr, exact rfl.ge,
      exact a_ge, exact a_le,},
    have temp':=add_le_add_right temp l,
    simp at temp', exact temp',
  },
end

lemma image_rec_right {b:ℝ}(r:ℝ){t:ℝ}(bt:b≤ t):
set.image (rec_right b r t) (set.interval 0 1)
⊆  {z:ℂ | b≤ z.im ∧ z.im≤ t ∧ z.re = r} :=
begin
  unfold rec_right, unfold line_segment,
  simp, unfold set.Icc, simp,
  intros a a_ge a_le, split,
  {
    apply mul_nonneg,
    simp, exact bt, exact a_ge,
  },
  {
    have temp: (t-b)*a≤ t-b :=
      by {apply mul_nonneg_le_one_le,
      simp, exact bt, exact rfl.ge,
      exact a_ge, exact a_le,},
    have temp':=add_le_add_right temp b,
    simp at temp', exact temp',
  },
end

lemma image_rec_top {r:ℝ}(t:ℝ){l:ℝ}(lr:l≤ r):
set.image (rec_top r t l) (set.interval 0 1)
⊆  {z:ℂ | l≤ z.re ∧ z.re≤ r ∧ z.im = t} :=
begin
  unfold rec_top, unfold line_segment,
  simp, unfold set.Icc, simp,
  intros a a_ge a_le, split,
  {
    have temp: (r-l)*a≤ r-l :=
      by {apply mul_nonneg_le_one_le,
      simp, exact lr, exact rfl.ge,
      exact a_ge, exact a_le,},
    have temp_m: -(r-l)≤ -((r-l)*a):=neg_le_neg temp,
    have temp':=add_le_add_right temp_m r,
    have rwl : -(r - l) + r = l:= by ring_nf,
    have rwr : -((r - l) * a) + r = (l - r) * a + r :=by ring_nf,
    rw [rwl, rwr] at temp', exact temp',
  },
  {
    apply mul_nonpos_of_nonpos_of_nonneg,
    simp, exact lr, exact a_ge,
  },
end

lemma image_rec_left {t:ℝ}(l:ℝ){b:ℝ}(bt:b≤ t):
set.image (rec_left t l b) (set.interval 0 1)
⊆  {z:ℂ | b≤ z.im ∧ z.im≤ t ∧ z.re = l} :=
begin
  unfold rec_left, unfold line_segment,
  simp, unfold set.Icc, simp,
  intros a a_ge a_le, split,
  {
    have temp: (t-b)*a≤ t-b :=
      by {apply mul_nonneg_le_one_le,
      simp, exact bt, exact rfl.ge,
      exact a_ge, exact a_le,},
    have temp_m: -(t-b)≤ -((t-b)*a):=neg_le_neg temp,
    have temp':=add_le_add_right temp_m t,
    have rwl : -(t - b) + t = b:= by ring_nf,
    have rwr : -((t - b) * a) + t = (b - t) * a + t :=by ring_nf,
    rw [rwl, rwr] at temp', exact temp',
  },
  {
    apply mul_nonpos_of_nonpos_of_nonneg,
    simp, exact bt, exact a_ge,
  },
end

lemma image_rectangle'{b r t l:ℝ}(bt: b≤ t)(lr: l≤ r):
set.image (rectangle b r t l) (set.interval 0 1)=
((set.image (rec_bottom l b r) (set.interval 0 1))∪
(set.image (rec_right b r t) (set.interval 0 1))) ∪
((set.image (rec_top r t l) (set.interval 0 1))∪
(set.image (rec_left t l b) (set.interval 0 1))):=
begin
  unfold rectangle,
  rw path_concatenation_image (br_join_tl b r t l),
  rw [rec_bottomright, rec_topleft],
  rw path_concatenation_image (bottom_join_right b r t l),
  rw path_concatenation_image (top_join_left b r t l),
end

lemma image_rectangle_sub_closure{b r t l:ℝ}
(bt: b≤ t)(lr: l≤ r):
set.image (rectangle b r t l) (set.interval 0 1)
⊆ (set.interval l r ×ℂ set.interval b t) :=
begin
  rw image_rectangle' bt lr,
  apply set.union_subset,
  apply set.union_subset,
  {
    intros x x_in,
    have x_in':=set.mem_of_subset_of_mem
      (image_rec_bottom b lr) x_in,
    simp at x_in',
    apply point_in_closure_rectangle,
    exact eq.ge x_in'.2.2,
    rw x_in'.2.2, exact bt,
    exact x_in'.1, exact x_in'.2.1,
  },
  {
    intros x x_in,
    have x_in':=set.mem_of_subset_of_mem
      (image_rec_right r bt) x_in,
    simp at x_in',
    apply point_in_closure_rectangle,
    exact x_in'.1, exact x_in'.2.1,
    rw x_in'.2.2, exact lr,
    exact (eq.symm x_in'.2.2).ge,
  },
  apply set.union_subset,
  {
    intros x x_in,
    have x_in':=set.mem_of_subset_of_mem
      (image_rec_top t lr) x_in,
    simp at x_in',
    apply point_in_closure_rectangle,
    rw x_in'.2.2, exact bt,
    exact (eq.symm x_in'.2.2).ge,
    exact x_in'.1, exact x_in'.2.1,
  },
  {
    intros x x_in,
    have x_in':=set.mem_of_subset_of_mem
      (image_rec_left l bt) x_in,
    simp at x_in',
    apply point_in_closure_rectangle,
    exact x_in'.1, exact x_in'.2.1,
    exact eq.ge x_in'.2.2,
    rw x_in'.2.2, exact lr,
  },
end

lemma image_rectangle_sub_compl_center{c: ℂ}
{b r t l:ℝ}(bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r):
set.image (rectangle b r t l) (set.interval 0 1) ⊆ {c}ᶜ :=
begin
  have bt: b≤ t:= le_of_lt (lt_trans bc ct),
  have lr: l≤ r:= le_of_lt (lt_trans lc cr),
  rw image_rectangle' bt lr,
  rw set.union_subset_iff, split,
  rw set.union_subset_iff, split,
  {
    intros x x_in, simp, intro x_c,
    have x_in':=set.mem_of_subset_of_mem
      (image_rec_bottom b lr) x_in,
    simp at x_in', rw x_c at x_in',
    rw x_in'.2.2 at bc, simp at bc, exact bc,
  },
  {
    intros x x_in, simp, intro x_c,
    have x_in':=set.mem_of_subset_of_mem
      (image_rec_right r bt) x_in,
    simp at x_in', rw x_c at x_in',
    rw x_in'.2.2 at cr, simp at cr, exact cr,
  },
  rw set.union_subset_iff, split,
  {
    intros x x_in, simp, intro x_c,
    have x_in':=set.mem_of_subset_of_mem
      (image_rec_top t lr) x_in,
    simp at x_in', rw x_c at x_in',
    rw x_in'.2.2 at ct, simp at ct, exact ct,
  },
  {
    intros x x_in, simp, intro x_c,
    have x_in':=set.mem_of_subset_of_mem
      (image_rec_left l bt) x_in,
    simp at x_in', rw x_c at x_in',
    rw x_in'.2.2 at lc, simp at lc, exact lc,
  },
end

lemma image_rectangle_sub_closure_inter_compl_center
{c: ℂ}{b r t l:ℝ}(bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r):
set.image (rectangle b r t l) (set.interval 0 1) ⊆
(set.interval l r ×ℂ set.interval b t) ∩ {c}ᶜ :=
begin
  rw set.subset_inter_iff, split,
  exact image_rectangle_sub_closure
    (le_of_lt (lt_trans bc ct)) (le_of_lt (lt_trans lc cr)),
  exact image_rectangle_sub_compl_center bc ct lc cr,
end

@[protected] lemma rec_bottomright_continuous_on(b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ):
continuous_on (rec_bottomright b r t l) (set.interval 0 1):=
path_concatenation_continuous_on
(bottom_join_right b r t l)
(line_is_continuous_on (l+b*complex.I) (r+b*complex.I))
(line_is_continuous_on (r+b*complex.I) (r+t*complex.I))

@[protected] lemma rec_topleft_continuous_on(b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ):
continuous_on (rec_topleft b r t l) (set.interval 0 1):=
path_concatenation_continuous_on
(top_join_left b r t l)
(line_is_continuous_on (r+t*complex.I) (l+t*complex.I))
(line_is_continuous_on (l+t*complex.I) (l+b*complex.I))

lemma rectangle_continuous_on(b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ):
continuous_on (rectangle b r t l) (set.interval 0 1):=
path_concatenation_continuous_on
(br_join_tl b r t l)
(rec_bottomright_continuous_on b r t l)
(rec_topleft_continuous_on b r t l)

@[protected] lemma deriv_rec_bottomright_integrable(b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ):
interval_integrable (deriv (rec_bottomright b r t l))
measure_theory.measure_space.volume 0 1 :=
path_concatenation_deriv_integrable
(bottom_join_right b r t l)
(deriv_line_integrable (l+b*complex.I) (r+b*complex.I))
(deriv_line_integrable (r+b*complex.I) (r+t*complex.I))

@[protected] lemma deriv_rec_topleft_integrable(b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ):
interval_integrable (deriv (rec_topleft b r t l))
measure_theory.measure_space.volume 0 1 :=
path_concatenation_deriv_integrable
(top_join_left b r t l)
(deriv_line_integrable (r+t*complex.I) (l+t*complex.I))
(deriv_line_integrable (l+t*complex.I) (l+b*complex.I))

lemma deriv_rectangle_integrable(b:ℝ)(r:ℝ)(t:ℝ)(l:ℝ):
interval_integrable (deriv (rectangle b r t l))
measure_theory.measure_space.volume 0 1 :=
path_concatenation_deriv_integrable
(br_join_tl b r t l)
(deriv_rec_bottomright_integrable b r t l)
(deriv_rec_topleft_integrable b r t l)

/-! Part III. Define integrals along rectangles

- # Integral along Rectangles
-/

def complex_affine (α:ℂ)(c:ℂ):ℂ→ ℂ:=λz, α*z+c

lemma contour_integral_under_affine (f:ℂ → E)
(L:ℝ → ℂ)(α:ℂ)(c:ℂ):
  contour_integral f ((complex_affine α c)∘ L) =
  α • contour_integral (f ∘ (complex_affine α c)) L :=
begin
  repeat {rw contour_integral},
  rw complex_affine,
  simp,
  rw smul_integral_convert,
  have l1: (λ t:ℝ, (α * deriv L t) • f (α * L t + c))
    = (λ t:ℝ, α •  deriv L t • f (α * L t + c)) :=
    by {ext1, rw smul_assoc_convert,},
  rw l1,
end

/- The contour integral along the sgements with both endpoints being real numbers is equal to the corresponding real integral. -/
lemma integral_along_reals (f:ℂ → E)(a:ℝ)(b:ℝ):
  contour_integral f (line_segment a b) =
  ∫ (t: ℝ ) in a..b, f(t):=
begin
  unfold contour_integral,
  rw [deriv_of_line, constant_path, line_segment],
  simp,
  have h0 : ↑(b - a) = (b : ℂ) - (a : ℂ) := by simp,
  rw ←h0,
  cases decidable.em (b ≠ a) with heq hneq,
  {
    have h1 : (λ (x : ℝ), f (↑(b - a) * (x : ℂ) + (a : ℂ))) = (λ (x : ℝ), ((f ∘ λ (y : ℝ), (y : ℂ)) ((b - a) * x + a))) := by simp,
    have h2 : (λ (t : ℝ), f (t : ℂ)) = (λ (x : ℝ), ((f ∘ λ (y : ℝ), (y : ℂ)) x)) := by simp,
    rw [h1, h2],
    let g := (f ∘ λ (y : ℝ), (y : ℂ)),
    have h3 : g = (f ∘ λ (y : ℝ), (y : ℂ)) := rfl,
    rw [←h3],
    have h4 : ∫ (x : ℝ) in a..b, g x = ∫ (x : ℝ) in (b - a) * 0 + a..(b - a) * 1 + a, g x := by simp,
    rw h4,
    rw ←interval_integral.smul_integral_comp_mul_add g (b - a) a,
    have h5 : ∫ (x : ℝ) in 0..1, g ((b - a) * x + a) = ∫ (x : ℝ) in 0..1, g ((b - a) * x + a) := rfl,
    rw smul_type_convert (b-a) ∫ (x : ℝ) in 0..1, g ((b - a) * x + a),
  },
  {
    simp at hneq,
    rw hneq,
    simp,
  }
end

lemma integral_along_horizontal_line (f:ℂ → E)(a:ℝ)(b:ℝ)(c:ℝ):
  contour_integral f (line_segment (a+c*complex.I) (b+c*complex.I)) =
  ∫ (t: ℝ) in a..b, f(t+c*complex.I) :=
begin
  have hr: ∫ (t: ℝ) in a..b, f(t+c*complex.I) =
    ∫ (t: ℝ) in a..b, (f∘ (complex_affine 1 (c*complex.I))) t:=
    by {rw complex_affine,simp,},
  have hl: (line_segment (↑a + ↑c * complex.I) (↑b + ↑c * complex.I))
    = (complex_affine 1 (c*complex.I)) ∘ (line_segment a b) :=
    by {rw complex_affine, repeat {rw line_segment,},
        simp, ext1, simp, ring_nf,},
  rw hr, rw hl,
  rw contour_integral_under_affine _ _ _,
  rw integral_along_reals _ a b,
  simp,
end

lemma integral_along_vertical_line (f:ℂ → E)(a:ℝ)(b:ℝ)(c:ℝ):
  contour_integral f (line_segment (c+a*complex.I) (c+b*complex.I)) =
  complex.I • ∫ (t: ℝ) in a..b, f(c+t*complex.I) :=
begin
  have hr: ((λ t:ℝ, f (↑c + ↑t * complex.I)):ℝ → E)
  = ((λt:ℝ, (f ∘ (complex_affine complex.I c)) t):ℝ→ E) :=
    by {rw complex_affine, simp, ext1, ring_nf,},
  have hl:(line_segment (↑c + ↑a * complex.I) (↑c + ↑b * complex.I))
    = (complex_affine complex.I c) ∘ (line_segment a b) :=
    by {repeat {rw complex_affine,}, repeat {rw line_segment,},
    simp, ext1, simp, ring_nf,},
  rw hr, rw hl,
  rw contour_integral_under_affine _ _ _,
  rw integral_along_reals _ a b,
end

lemma integral_along_rectangle_bottom(f:ℂ → E)
(l:ℝ)(b:ℝ)(r:ℝ):
  contour_integral f (rec_bottom l b r)
  = ∫ (x: ℝ) in l..r, f(x+b*complex.I) :=
  integral_along_horizontal_line f l r b

lemma integral_along_rectangle_right(f:ℂ → E)
(b:ℝ)(r:ℝ)(t:ℝ):
  contour_integral f (rec_right b r t)
  = complex.I • ∫ (x: ℝ) in b..t, f(r+x*complex.I) :=
  integral_along_vertical_line f b t r

lemma integral_along_rectangle_top(f:ℂ → E)
(r:ℝ)(t:ℝ)(l:ℝ):
  contour_integral f (rec_top r t l)
  = - ∫ (x: ℝ) in l..r, f(x+t*complex.I) :=
  by { unfold rec_top,
       rw integral_along_horizontal_line f r l t,
       rw interval_integral.integral_symm, }

lemma integral_along_rectangle_left(f:ℂ → E)
(t:ℝ)(l:ℝ)(b:ℝ):
  contour_integral f (rec_left t l b)
  = - complex.I • ∫ (x: ℝ) in b..t, f(l+x*complex.I) :=
  by { unfold rec_left,
       rw integral_along_vertical_line f t b l,
       rw interval_integral.integral_symm, simp, }

@[protected] lemma integral_along_rectangle_bottomright'
{f:ℂ → E}{b r t l: ℝ}
(hf: continuous_on f
  (set.image (rec_bottomright b r t l) (set.interval 0 1))):
  contour_integral f (rec_bottomright b r t l)
  = (contour_integral f (rec_bottom l b r))
  + (contour_integral f (rec_right b r t)):=
contour_integral_along_piecewise_path' hf
(rec_bottomright_continuous_on b r t l)
(deriv_rec_bottomright_integrable b r t l)

@[protected] lemma integral_along_rectangle_topleft'
{f:ℂ → E}{b r t l: ℝ}
(hf: continuous_on f
  (set.image (rec_topleft b r t l) (set.interval 0 1))):
  contour_integral f (rec_topleft b r t l)
  = (contour_integral f (rec_top r t l))
  + (contour_integral f (rec_left t l b)):=
contour_integral_along_piecewise_path' hf
(rec_topleft_continuous_on b r t l)
(deriv_rec_topleft_integrable b r t l)

theorem integral_along_rectangle'
{f:ℂ → E}{b r t l: ℝ}
(hf: continuous_on f
  (set.image (rectangle b r t l) (set.interval 0 1))):
  contour_integral f (rectangle b r t l)
  = (((contour_integral f (rec_bottom l b r))
  + (contour_integral f (rec_top r t l)))
  + (contour_integral f (rec_right b r t)))
  + (contour_integral f (rec_left t l b)) :=
begin
  unfold rectangle,
  rw contour_integral_along_piecewise_path' hf
     (rectangle_continuous_on b r t l)
     (deriv_rectangle_integrable b r t l),
  have hfbr:=continuous_on.mono hf
       (path_concatenation_image_left_subset (br_join_tl b r t l)),
  have hftl:=continuous_on.mono hf
       (path_concatenation_image_right_subset (br_join_tl b r t l)),
  rw integral_along_rectangle_bottomright' hfbr,
  rw integral_along_rectangle_topleft' hftl,
  rw ← add_assoc (contour_integral f (rec_bottom l b r) +
  contour_integral f (rec_right b r t)) _ _,
  rw add_assoc _ (contour_integral f (rec_right b r t))
  (contour_integral f (rec_top r t l) ),
  rw add_comm (contour_integral f (rec_right b r t))
  (contour_integral f (rec_top r t l) ),
  rw ← add_assoc (contour_integral f (rec_bottom l b r)) _ _,
end

theorem integral_along_rectangle
{f:ℂ → E}{b r t l: ℝ}
(hf: continuous_on f
  (set.image (rectangle b r t l) (set.interval 0 1))):
  contour_integral f (rectangle b r t l)
  = (((∫ (x: ℝ) in l..r, f(x+b*complex.I))
  - (∫ (x: ℝ) in l..r, f(x+t*complex.I)))
  + (complex.I • ∫ (x: ℝ) in b..t, f(r+x*complex.I)))
  - (complex.I • ∫ (x: ℝ) in b..t, f(l+x*complex.I)) :=
begin
  rw integral_along_rectangle' hf,
  rw integral_along_rectangle_bottom,
  rw integral_along_rectangle_top,
  rw integral_along_rectangle_right,
  rw integral_along_rectangle_left,
  simp,
end

/-! Part IV. Formalize the Cauchy theorem on rectangles.

- # Cauchy Theorem on Rectangles
-/

theorem Cauchy_Goursat_rectangle_countable {f : ℂ → E}
{b r t l:ℝ}(bt: b≤ t)(lr: l≤ r)
{s: set ℂ}(hs: s.countable)
(Hc : continuous_on f (set.interval l r ×ℂ set.interval b t))
(Hd : ∀ (x : ℂ), x ∈ (set.Ioo l r ×ℂ set.Ioo b t) \ s
→ differentiable_at ℂ f x) :
contour_integral f (rectangle b r t l) = 0 :=
begin
  have hf: continuous_on f
       (set.image (rectangle b r t l) (set.interval 0 1)):=
       continuous_on.mono Hc (image_rectangle_sub_closure bt lr),
  rw integral_along_rectangle hf,
  let z:ℂ:={re:=l,im:=b},
  let w:ℂ:={re:=r,im:=t},
  have z_re : l = z.re := rfl,
  have w_re : r = w.re := rfl,
  have z_im : b = z.im := rfl,
  have w_im : t = w.im := rfl,
  have hl : l = linear_order.min z.re w.re :=
    by {rw [←z_re, ←w_re], symmetry, exact min_eq_left lr,},
  have hr : r = linear_order.max z.re w.re :=
    by {rw [←z_re, ←w_re], symmetry, exact max_eq_right lr,},
  have hb : b = linear_order.min z.im w.im :=
    by {rw [←z_im, ←w_im], symmetry, exact min_eq_left bt,},
  have ht : t = linear_order.max z.im w.im :=
    by {rw [←z_im, ←w_im], symmetry, exact max_eq_right bt,},
  rw [z_re, w_re, z_im, w_im] at Hc,
  rw [hl, hr, hb, ht] at Hd,
  have t:=complex.integral_boundary_rect_eq_zero_of_differentiable_on_off_countable
           f z w s hs Hc Hd,
  exact t,
end

theorem Cauchy_Goursat_rectangle_singleton {f : ℂ → E}
(c: ℂ) {b r t l:ℝ}(bt: b≤ t)(lr: l≤ r)
(Hc : continuous_on f (set.interval l r ×ℂ set.interval b t))
(Hd : ∀ (x : ℂ), x ∈ (set.Ioo l r ×ℂ set.Ioo b t) \ {c}
→ differentiable_at ℂ f x) :
contour_integral f (rectangle b r t l) = 0 :=
  Cauchy_Goursat_rectangle_countable
    bt lr (set.to_countable {c}) Hc Hd

theorem Cauchy_Goursat_rectangle{f : ℂ → E}
{b r t l:ℝ}(bt: b≤ t)(lr: l≤ r)
(Hc : continuous_on f (set.interval l r ×ℂ
  set.interval b t))
(Hd : ∀ (x : ℂ), x ∈ (set.Ioo l r ×ℂ set.Ioo b t)
→ differentiable_at ℂ f x) :
contour_integral f (rectangle b r t l) = 0 :=
begin
  apply Cauchy_Goursat_rectangle_singleton 0 bt lr Hc,
  intros x x_in, simp at x_in,
  exact Hd x x_in.1,
end

/-! Part V. Formalize the Cauchy integral formula on rectangles.

- # Cauchy Integral Formula on Rectangles
-/

lemma dslope_eq_on{f : ℂ → E}{c: ℂ}
{b r t l:ℝ}(bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r):
set.eq_on (dslope f c) (λz:ℂ, (z-c)⁻¹•f(z) - (z-c)⁻¹•f(c))
(set.image (rectangle b r t l) (set.interval 0 1)):=
begin
  apply set.eq_on.mono
    (image_rectangle_sub_compl_center bc ct lc cr),
  have func_eq:(λz:ℂ, (z-c)⁻¹•f(z) - (z-c)⁻¹•f(c))=
    slope f c :=
    by {ext1, rw slope_def_module f c x, rw smul_sub,},
  rw func_eq,
  exact eq_on_dslope_slope f c,
end

lemma dslope_continuous_on {f : ℂ → E}{c: ℂ}
{b r t l:ℝ}(bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r)
(Hc : continuous_on f (set.interval l r ×ℂ set.interval b t))
(Hd : differentiable_on ℂ f (set.Ioo l r ×ℂ set.Ioo b t)):
continuous_on (dslope f c) (set.interval l r ×ℂ set.interval b t):=
begin
  rw continuous_on_dslope
    (closure_rectangle_neighborhood' bc ct lc cr),
  split,
  exact Hc,
  exact differentiable_on.differentiable_at Hd
    (interior_rectangle_neighborhood' bc ct lc cr),
end

lemma dslope_differentiable_at {f : ℂ → E}{c: ℂ}
{b r t l:ℝ}(bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r)
(Hc : continuous_on f (set.interval l r ×ℂ set.interval b t))
(Hd : differentiable_on ℂ f (set.Ioo l r ×ℂ set.Ioo b t)):
∀ (x : ℂ), x ∈ set.Ioo l r ×ℂ set.Ioo b t \ {c} →
differentiable_at ℂ (dslope f c) x :=
begin
  intros x x_in,
  simp at x_in,
  rw differentiable_at_dslope_of_ne x_in.2,
  have hd:=Hd x x_in.1,
  exact differentiable_within_at.differentiable_at hd
    (interior_rectangle_neighborhood x_in.1),
end

lemma dslope_zero_integral {f : ℂ → E} {c: ℂ}
{b r t l:ℝ} (bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r)
(Hc : continuous_on f (set.interval l r ×ℂ set.interval b t))
(Hd : differentiable_on ℂ f (set.Ioo l r ×ℂ set.Ioo b t)):
contour_integral (dslope f c) (rectangle b r t l) = 0 :=
begin
  have b_lt_t : b<t := lt_trans bc ct,
  have l_lt_r : l<r := lt_trans lc cr,
  have bt: b≤ t:= le_of_lt b_lt_t,
  have lr: l≤ r:= le_of_lt l_lt_r,
  apply Cauchy_Goursat_rectangle_singleton c bt lr,
  exact dslope_continuous_on bc ct lc cr Hc Hd,
  exact dslope_differentiable_at bc ct lc cr Hc Hd,
end

lemma part_of_dslope_continuous_on{f : ℂ → E} {c: ℂ}
{b r t l:ℝ} (bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r)
(Hc : continuous_on f (set.interval l r ×ℂ set.interval b t)):
continuous_on ((λ (z : ℂ), (z - c)⁻¹) • f)
(rectangle b r t l '' set.interval 0 1) :=
begin
  have hr: continuous_on (λ (z : ℂ), (z - c)⁻¹)
    (rectangle b r t l '' set.interval 0 1) :=
    continuous_on.mono (reciprocal_continuous_on c)
      (image_rectangle_sub_compl_center bc ct lc cr),
  have ss :(set.interval l r ×ℂ set.interval b t) ∩ {c}ᶜ
  ⊆ (set.interval l r ×ℂ set.interval b t) :=
  (set.interval l r ×ℂ set.interval b t).inter_subset_left {c}ᶜ,
  have hf': continuous_on f
    ((set.interval l r ×ℂ set.interval b t) ∩ {c}ᶜ) :=
    continuous_on.mono Hc ss,
  have hf: continuous_on f
    (rectangle b r t l '' set.interval 0 1) :=
    continuous_on.mono hf'
    (image_rectangle_sub_closure_inter_compl_center bc ct lc cr),
  have rf:= continuous_on.prod_map hr hf,
  exact continuous_on.smul hr hf,
end

lemma Cauchy_integral_formula_rectangle_pre{f : ℂ → E} {c: ℂ}
{b r t l:ℝ} (bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r)
(Hc : continuous_on f (set.interval l r ×ℂ set.interval b t))
(Hd : differentiable_on ℂ f (set.Ioo l r ×ℂ set.Ioo b t)):
contour_integral (λz:ℂ, (z-c)⁻¹•f(z)) (rectangle b r t l)=
contour_integral (λz:ℂ, (z-c)⁻¹) (rectangle b r t l) • f(c):=
begin
  rw ← contour_integral_smul_right _ _ (f(c)),
  have func_eq:(λz:ℂ, (z-c)⁻¹•f(z) - (z-c)⁻¹•f(c))=
  (λz:ℂ, (z-c)⁻¹•f(z))-(λz:ℂ, (z-c)⁻¹•f(c)):=
    by {ext1, simp,},
  have left_func:(λ (z : ℂ), (z - c)⁻¹ • f z) =
  (λ (z : ℂ), (z - c)⁻¹) • f:=
    by {ext1, simp,},
  have right_func:(λ (z : ℂ), (z - c)⁻¹ • f c) =
  (λ (z : ℂ), (z - c)⁻¹) • (λ (z:ℂ), f c) :=
    by {ext1, simp,},
  have int_z:=dslope_zero_integral bc ct lc cr Hc Hd,
  rw contour_integral_congr (dslope_eq_on bc ct lc cr) at int_z,
  rw func_eq at int_z,
  have int_sub:contour_integral ((λ (z : ℂ), (z - c)⁻¹ • f z) -
  λ (z : ℂ), (z - c)⁻¹ • f c) (rectangle b r t l) =
  contour_integral (λ (z : ℂ), (z - c)⁻¹ • f z) (rectangle b r t l) -
  contour_integral (λ (z : ℂ), (z - c)⁻¹ • f c) (rectangle b r t l):=
    begin
      apply contour_integral_sub',
      {
        rw left_func,
        exact part_of_dslope_continuous_on bc ct lc cr Hc,
      },
      {
        rw right_func,
        apply part_of_dslope_continuous_on bc ct lc cr,
        apply continuous.continuous_on,
        exact continuous_const,
        exact _inst_3,
      },
      {
        exact rectangle_continuous_on b r t l,
      },
      {
        exact deriv_rectangle_integrable b r t l,
      },
    end,
  rw int_z at int_sub,
  exact zero_exact int_sub,
  exact _inst_3,
end

lemma log_comp_affine_continuous_on{a b:ℂ}{s:set ℝ}
(ha: a ≠ 0)
(h: ∀ (x:ℝ), x∈ s→ 0 < (a*x+b).re ∨ (a*x+b).im ≠ 0):
continuous_on (λ (t : ℝ), (a⁻¹*complex.log (a*t + b))) s:=
begin
  apply continuous_on.const_smul _ a⁻¹,
  exact is_scalar_tower.has_continuous_const_smul,
  apply continuous_on.clog,
  exact continuous.continuous_on (affine_rtc_continuous a b),
  exact h,
end

lemma log_comp_affine_has_deriv {a b:ℂ}{x:ℝ}
(ha: a ≠ 0)(h: 0 < (a*x+b).re ∨ (a*x+b).im ≠ 0):
has_deriv_at (λ (t : ℝ), (a⁻¹*complex.log (a*t + b)))
((a*x+b)⁻¹) x :=
begin
  have funrw: (λ (t : ℝ), (a⁻¹*complex.log (a*t + b)))
  =(λ (t : ℝ), (complex.log (a*t + b))*a⁻¹):=
    by {ext1, rw mul_comm,},
  rw funrw,
  have axbrw: (a*x+b)⁻¹ = (a*(a*x+b)⁻¹)*a⁻¹:=
    by {rw mul_comm _ (a*x+b)⁻¹, rw mul_assoc,
        have a':a * a⁻¹=1:= div_self ha,
        rw a', ring_nf,},
  rw axbrw,
  apply has_deriv_at.mul_const _ a⁻¹,
  let f:ℝ→ ℂ:=λt:ℝ, (a*t+b),
  have f_rw: (λ (y : ℝ), complex.log (a * y + b))
  =(λ (t:ℝ), complex.log (f t)) :=
    by {ext1, simp,},
  have f'f_rw: (a * (a * ↑x + b)⁻¹) = a/f x:=
    by {ring_nf,simp,left,rw mul_comm,},
  rw [f_rw, f'f_rw],
  apply has_deriv_at.clog_real,
  simp,
  have coe_comp: f=(λt:ℂ, a*t+b) ∘ (λ (t : ℝ), (t : ℂ)):=
    by {ext1, simp,},
  rw coe_comp,
  have conc:has_deriv_at ((λ (t : ℂ), a * t + b) ∘
    λ (t : ℝ), ↑t) a x ↔
    has_deriv_at ((λ (t : ℂ), a * t + b) ∘
    λ (t : ℝ), ↑t) (a*1) x := by simp,
  rw conc,
  apply has_deriv_at.comp,
  exact complex_affine_has_deriv a b _,
  exact coe_has_deriv _,
  exact h,
end

lemma integral_of_fraction'{a b:ℂ}{lef ref:ℝ}
(ha: a ≠ 0)(hlr: lef ≤ ref)
(h: ∀ (x:ℝ), (x∈ (set.Ioo lef ref)) →
0 < (a*x+b).re ∨ (a*x+b).im ≠ 0)
(hc:continuous_on  (λ x : ℝ, a⁻¹ * complex.log (a * x + b))
(set.Icc lef ref))
(hii: interval_integrable (λ (y : ℝ), (a * ↑y + b)⁻¹)
measure_theory.measure_space.volume lef ref):
∫ (t: ℝ) in lef..ref, ((a*t+b)⁻¹) =
a⁻¹*(complex.log (a*ref + b)-complex.log(a*lef+b)):=
begin
  rw mul_sub,
  apply interval_integral.integral_eq_sub_of_has_deriv_at_of_le hlr,
  exact hc,
  intros x x_in,
  have h':= h x x_in,
  exact log_comp_affine_has_deriv ha h',
  exact hii,
end

lemma integral_of_fraction{a b:ℂ}{lef ref:ℝ}
(ha: a ≠ 0)(hlr: lef ≤ ref)
(h: ∀ (x:ℝ), (x∈ (set.Icc lef ref)) →
0 < (a*x+b).re ∨ (a*x+b).im ≠ 0):
∫ (t: ℝ) in lef..ref, ((a*t+b)⁻¹) =
a⁻¹*(complex.log (a*ref + b)-complex.log(a*lef+b)):=
begin
  apply integral_of_fraction' ha hlr,
  intros x x_in,
  exact h x (set.Ioo_subset_Icc_self x_in),
  exact log_comp_affine_continuous_on ha h,
  apply continuous_on.interval_integrable,
  apply affine_rtc_continuous_on,
  intros x x_in,
  unfold set.interval at x_in,
  have lef_rw:(min lef ref)=lef:= min_eq_left hlr,
  have ref_rw:(max lef ref)=ref:= max_eq_right hlr,
  rw [lef_rw, ref_rw] at x_in,
  have h'':=h x x_in, intro f,
  rw f at h'',
  simp at h'', exact h'',
end

lemma integral_of_fraction_one{b:ℂ}{lef ref:ℝ}
(hlr: lef < ref)
(h: ∀ (x:ℝ), (x∈ (set.Icc lef ref)) →
0 < ((x:ℂ)+b).re ∨ ((x:ℂ)+b).im ≠ 0):
∫ (t: ℝ) in lef..ref, (((t:ℂ)+b)⁻¹) =
(complex.log (ref + b)-complex.log(lef+b)) :=
begin
  have one_ne_zero:(1:ℂ)≠ 0:=by simp,
  have hlr':lef≤ ref:= le_of_lt hlr,
  have h':∀ (x:ℝ), (x∈ (set.Icc lef ref)) →
    0 < ((1:ℂ)*x+b).re ∨ ((1:ℂ)*x+b).im ≠ 0 :=
    by {ring_nf,exact h,},
  have lhs:∫ (t: ℝ) in lef..ref, (((t:ℂ)+b)⁻¹) =
  ∫ (t: ℝ) in lef..ref, (((1:ℂ)*t+b)⁻¹):= by simp,
  rw lhs,
  rw integral_of_fraction one_ne_zero hlr' h',
  simp,
end

lemma integral_of_fraction_I'{b:ℂ}{lef ref:ℝ}
(hlr: lef < ref)
(h: ∀ (x:ℝ), (x∈ (set.Ioo lef ref)) →
0 < (complex.I*x+b:ℂ).re ∨ (complex.I*x+b:ℂ).im ≠ 0)
(hc:continuous_on  (λ x : ℝ, complex.log (complex.I * x + b))
(set.Icc lef ref))
(hii: interval_integrable (λ (y : ℝ), (complex.I * y + b)⁻¹)
measure_theory.measure_space.volume lef ref):
complex.I • ∫ (t: ℝ) in lef..ref, ((complex.I*t+b:ℂ)⁻¹) =
(complex.log (complex.I * ref + b)) -
(complex.log (complex.I * lef + b)) :=
begin
  have i_ne_zero:complex.I≠ 0:=complex.I_ne_zero,
  have hlr':lef≤ ref:= le_of_lt hlr,
  have hc':continuous_on  (λ x : ℝ,
    (complex.I)⁻¹*complex.log (complex.I * x + b))
    (set.Icc lef ref):=
      by { have hcm:=
      continuous_on.const_smul hc (complex.I)⁻¹,
      simp, simp at hcm, exact hcm,},
  rw integral_of_fraction' i_ne_zero hlr' h hc' hii,
  simp, rw ← mul_assoc, simp,
end

lemma integral_of_fraction_I{b:ℂ}{lef ref:ℝ}
(hlr: lef < ref)
(h: ∀ (x:ℝ), (x∈ (set.Icc lef ref)) →
0 < (x*complex.I+b:ℂ).re ∨ (x*complex.I+b:ℂ).im ≠ 0):
complex.I • ∫ (t: ℝ) in lef..ref, ((t*complex.I+b:ℂ)⁻¹) =
(complex.log (ref*complex.I + b)) -
(complex.log (lef*complex.I + b)) :=
begin
  have i_ne_zero:complex.I≠ 0:=complex.I_ne_zero,
  have hlr':lef≤ ref:= le_of_lt hlr,
  have h':∀ (x:ℝ), (x∈ (set.Icc lef ref)) →
    0 < (complex.I*x+b).re ∨ (complex.I*x+b).im ≠ 0 :=
    by {ring_nf,exact h,},
  have lhs:(λt:ℝ,(t*complex.I+b:ℂ)⁻¹) =
    (λt:ℝ ,(complex.I*t+b)⁻¹):=
    by {ext1,simp,rw mul_comm},
  rw lhs,
  rw integral_of_fraction i_ne_zero hlr' h',
  simp, rw ← mul_assoc, simp,
  rw mul_comm, rw mul_comm ↑lef complex.I,
end

lemma integral_of_reciprocal_on_bottom {c: ℂ}
{l b r:ℝ} (bc: b < c.im) (lc: l < c.re) (cr: c.re < r):
contour_integral (λz:ℂ, (z-c)⁻¹) (rec_bottom l b r) =
complex.log (r+b*complex.I-c) -
complex.log (l+b*complex.I-c) :=
begin
  have lr:l< r:= lt_trans lc cr,
  rw integral_along_rectangle_bottom,
  have lhs: (λx:ℝ,(↑x + ↑b * complex.I - c)⁻¹)=
  (λx:ℝ,(↑x + (↑b * complex.I - c))⁻¹) :=
    by {ext1, rw← add_sub,}, rw lhs,
  repeat {rw ← add_sub},
  apply integral_of_fraction_one lr,
  intros x x_in, rw add_sub,
  simp, right, intro f,
  exact (ne_of_lt bc) (zero_symm_exact f),
end

lemma integral_of_reciprocal_on_top {c: ℂ}
{r t l:ℝ} (ct: c.im < t) (lc: l < c.re) (cr: c.re < r):
contour_integral (λz:ℂ, (z-c)⁻¹) (rec_top r t l) =
complex.log (l+t*complex.I-c) -
complex.log (r+t*complex.I-c) :=
begin
  have lr:l< r:= lt_trans lc cr,
  rw integral_along_rectangle_top,
  rw neg_rewrite, simp,
  have lhs: (λx:ℝ,(↑x + ↑t * complex.I - c)⁻¹)=
  (λx:ℝ,(↑x + (↑t * complex.I - c))⁻¹) :=
    by {ext1, rw← add_sub,}, rw lhs,
  repeat {rw ← add_sub},
  apply integral_of_fraction_one lr,
  intros x x_in, rw add_sub,
  simp, right, intro f,
  have f':= zero_symm_exact f,
  rw f' at ct, simp at ct, exact ct,
end

lemma integral_of_reciprocal_on_right {c: ℂ}
{b r t:ℝ} (bc: b < c.im) (ct: c.im < t) (cr: c.re < r) :
contour_integral (λz:ℂ, (z-c)⁻¹) (rec_right b r t) =
complex.log (r+t*complex.I-c) -
complex.log (r+b*complex.I-c) :=
begin
  have bt : b< t:= lt_trans bc ct,
  rw integral_along_rectangle_right,
  have lhs: (λx:ℝ,(↑r + ↑x * complex.I - c)⁻¹)=
  (λx:ℝ,( ↑x * complex.I +(↑r- c))⁻¹) :=
    by {ext1, ring_nf,}, rw lhs,
  have rtc: ↑r + ↑t * complex.I - c =
    ↑t * complex.I+ (↑r-c):= by ring_nf,
  have rbc: ↑r + ↑b * complex.I - c =
    ↑b * complex.I + (↑r - c) := by ring_nf,
  rw [rtc, rbc],
  apply integral_of_fraction_I bt,
  intros x x_in, simp,
  left, exact cr,
end

@[protected] lemma integrable_lxc_inv_bt{c:ℂ}{t l b:ℝ}
(bc: b < c.im) (ct: c.im < t) (lc: l < c.re):
interval_integrable (λ (x : ℝ),
(complex.I * ↑x  + (↑l - c))⁻¹)
measure_theory.measure_space.volume b t :=
begin
  apply continuous_on.interval_integrable,
  apply affine_rtc_continuous_on,
  intros x x_in, intro fp,
  have rp:(complex.I * ↑x + (↑l - c)).re=l-c.re:=
    by simp,
  rw fp at rp, simp at rp,
  exact (ne_of_lt lc) (zero_exact rp),
end

@[protected] lemma integrable_lxc_inv_bcim{c:ℂ}{t l b:ℝ}
(bc: b < c.im) (ct: c.im < t) (lc: l < c.re):
interval_integrable (λ (x : ℝ),
(complex.I * ↑x  + (↑l - c))⁻¹)
measure_theory.measure_space.volume b c.im :=
begin
  have bt: b< t:= (lt_trans bc ct),
  apply interval_integrable.mono_set
    (integrable_lxc_inv_bt bc ct lc),
  unfold set.interval,
  rw [min_eq_left_of_lt bc, min_eq_left_of_lt bt,
    max_eq_right_of_lt bc, max_eq_right_of_lt bt],
  exact set.Icc_subset_Icc_right (le_of_lt ct),
end

@[protected] lemma integrable_lxc_inv_cimt{c:ℂ}{t l b:ℝ}
(bc: b < c.im) (ct: c.im < t) (lc: l < c.re):
interval_integrable (λ (x : ℝ),
(complex.I * ↑x  + (↑l - c))⁻¹)
measure_theory.measure_space.volume c.im t :=
begin
  have bt: b< t:= (lt_trans bc ct),
  apply interval_integrable.mono_set
    (integrable_lxc_inv_bt bc ct lc),
  unfold set.interval,
  rw [min_eq_left_of_lt ct, min_eq_left_of_lt bt,
    max_eq_right_of_lt ct, max_eq_right_of_lt bt],
  exact set.Icc_subset_Icc_left (le_of_lt bc),
end

@[protected] lemma integral_left_two_pieces{c:ℂ}{t l b:ℝ}
(bc: b < c.im) (ct: c.im < t) (lc: l < c.re):
contour_integral (λz:ℂ, (z-c)⁻¹) (rec_left t l b)
= -(complex.I • ∫ (x: ℝ) in (c.im)..t, (l+x*complex.I-c)⁻¹)
- (complex.I • ∫ (x: ℝ) in b..(c.im), (l+x*complex.I-c)⁻¹):=
begin
  rw integral_along_rectangle_left,
  have lhs:-complex.I • ∫ (x : ℝ) in b..t,
  (↑l + ↑x * complex.I - c)⁻¹=-(complex.I •
  ∫ (x : ℝ) in b..t, (↑l + ↑x * complex.I - c)⁻¹):=
    by {simp,}, rw lhs,
  have fr: (λ (x : ℝ), (↑l + ↑x * complex.I - c)⁻¹)
  =(λ (x : ℝ), (complex.I * ↑x  + (↑l - c))⁻¹) :=
    by {ext1,simp,ring_nf,}, rw fr,
  rw neg_rewrite, simp, symmetry,
  rw ← mul_add, simp, left,
  exact interval_integral.integral_add_adjacent_intervals
    (integrable_lxc_inv_bcim bc ct lc)
    (integrable_lxc_inv_cimt bc ct lc),
end

@[protected] lemma crel{c:ℂ}{t l b:ℝ}
(bc: b < c.im) (ct: c.im < t) (lc: l < c.re):
c.re - l = complex.abs(l-c.re):=
begin
  have cplx:complex.abs(l-c.re)=
    complex.abs((l-c.re):ℝ):= by simp,
  rw cplx,
  have l_sub:(((l-c.re):ℝ):ℂ)=-(((c.re-l):ℝ):ℂ):= by simp,
  rw l_sub, rw complex.abs_neg,
  have c_sub:c.re-l≥ 0:=
    by {simp,exact le_of_lt lc,},
  exact (complex.abs_of_nonneg c_sub).symm,
end

@[protected] lemma lcre{c:ℂ}{t l b:ℝ}
(bc: b < c.im) (ct: c.im < t) (lc: l < c.re):
↑l - ↑(c.re)=complex.I*c.im+(l-c:ℂ) :=
begin
  apply complex.ext,
  simp, simp,
end

@[protected] lemma lcrearg{c:ℂ}{t l b:ℝ}
(bc: b < c.im) (ct: c.im < t) (lc: l < c.re):
(l - (c.re):ℂ).arg=real.pi :=
begin
  rw complex.arg_eq_pi_iff, split,
  simp, exact lc, simp,
end

lemma integral_on_lower_left{c:ℂ}{t l b:ℝ}
(bc: b < c.im) (ct: c.im < t) (lc: l < c.re):
∫ (x: ℝ) in b..(c.im), (l+x*complex.I-c:ℂ)⁻¹=
(complex.I)⁻¹ *(real.log(c.re-l)-real.pi*complex.I-
complex.log (l+b*complex.I-c)) :=
begin
  rw mul_sub,
  have lhs:(λx:ℝ,(↑l + ↑x * complex.I - c)⁻¹)=
  (λx:ℝ,( complex.I * ↑x + (l - c))⁻¹) :=
    by {ext1, simp, ring_nf,}, rw lhs,
  have rhs: ↑l + ↑b * complex.I - c =
    complex.I * ↑b + (↑l - c) := by ring_nf, rw rhs,
  let F:ℝ → ℂ:=λ (x : ℝ),
    ite (x=c.im) (complex.I⁻¹ *
    (↑(real.log (c.re - l)) - ↑real.pi * complex.I))
    (complex.I⁻¹*complex.log (complex.I*x + (l-c))),
  have Fx:∀x:ℝ, F x=ite (x=c.im) (complex.I⁻¹ *
    (↑(real.log (c.re - l)) - ↑real.pi * complex.I))
    (complex.I⁻¹*complex.log (complex.I*x + (l-c))):=
    by {intro x,exact rfl,},
  have Fcim: F c.im = (complex.I⁻¹ *
    (↑(real.log (c.re - l)) - ↑real.pi * complex.I)):=
    by {rw Fx c.im, simp,}, rw ← Fcim,
  have Fb: F b = complex.I⁻¹ *
    complex.log (complex.I * ↑b + (↑l - c)) :=
    by {rw Fx b, rw if_neg (ne_of_lt bc),}, rw ← Fb,
  have F_eq_on_Ico: set.eq_on
    (λx:ℝ, complex.I⁻¹*complex.log (complex.I*x + (l-c)))
    F (set.Ico b c.im):=
    by {intros x x_in, simp at x_in,
      have mh:=ne_of_lt x_in.2, rw Fx x,
      rw if_neg mh,},
  have F_eq_on_Ioo:=set.eq_on.mono
    set.Ioo_subset_Ico_self F_eq_on_Ico,
  apply interval_integral.integral_eq_sub_of_has_deriv_at_of_le
    (le_of_lt bc),
  {
    intros x x_in,
    by_cases x=c.im,
    {
      rw← continuous_within_at_diff_self ,
      have iccico:(set.Icc b c.im \ {x})=set.Ico b c.im:=
        by { rw h, exact set.Icc_diff_right,},
      rw iccico,
      unfold continuous_within_at,
      apply tendsto_nhds_within_congr F_eq_on_Ico,
      rw h, rw Fcim,
      have indu: (λ (x : ℝ), (λ (x : ℝ), complex.I⁻¹ *
      complex.log (complex.I * ↑x + (↑l - c))) x) =
      (λ (x : ℝ), complex.I⁻¹ *
      complex.log (complex.I * ↑x + (↑l - c))):= by simp,
      rw indu,
      apply filter.tendsto.const_mul complex.I⁻¹,
      have ftr:(λ (k : ℝ), complex.log (complex.I * ↑k +
      (↑l - c)))= complex.log ∘ (λk:ℝ, (complex.I * ↑k +
      (↑l - c))):= by simp, rw ftr,
      apply filter.tendsto.comp,
      rw crel bc ct lc,
      apply complex.tendsto_log_nhds_within_im_neg_of_re_neg_of_im_zero,
      simp, exact lc,
      simp,
      apply tendsto_nhds_within_of_tendsto_nhds_of_eventually_within
        (λ (k : ℝ), complex.I * ↑k + (↑l - c)),
      rw lcre bc ct lc,
      apply filter.tendsto.add_const (l-c:ℂ),
      apply filter.tendsto.const_mul complex.I,
      have hs:=coe_differentiable.continuous.continuous_within_at.tendsto,
      simp at hs, simp, exact hs,
      simp, rw eventually_nhds_within_iff,
      rw eventually_nhds_iff,
      use set.univ, split,
      intros new_x x_in_1 x_in_2,
      simp at x_in_2, exact x_in_2.2,
      split, exact is_open_univ,
      exact set.mem_univ c.im,
    },
    {
      have x_now_in:x∈ set.Ico b c.im :=
        by {unfold set.Ico, simp,
          unfold set.Icc at x_in, simp at x_in,
          split, exact x_in.1,
          exact ne.lt_of_le h x_in.2,},
      have Iconhd:set.Ico b c.im∈ nhds_within x
        (set.Icc b c.im):=
        by {rw mem_nhds_within, use (set.Iio c.im), split,
        exact is_open_Iio, split,
        exact set.Ico_subset_Iio_self x_now_in,
        exact eq.subset Iio_inter_Icc,} ,
      rw ← continuous_within_at_inter' Iconhd,
      rw set.inter_comm,
      rw set.inter_eq_left_iff_subset.2
        set.Ico_subset_Icc_self,
      apply continuous_on.continuous_within_at _ x_now_in,
      apply continuous_on.congr _
        (set.eq_on.symm F_eq_on_Ico),
      apply log_comp_affine_continuous_on complex.I_ne_zero,
      intros xx xx_in, simp, right, intro xf,
      simp at xx_in,
      exact (ne_of_lt xx_in.2) (zero_symm_exact xf),
    },
  },
  {
    intros x x_in,
    apply has_eq_deriv_on_Ioo (le_of_lt bc) F_eq_on_Ioo x_in,
    apply log_comp_affine_has_deriv complex.I_ne_zero,
    simp, right, intro ff, simp at x_in,
    exact (ne_of_lt x_in.2) (zero_symm_exact ff),
  },
  {
    exact (integrable_lxc_inv_bcim bc ct lc),
  },
end

lemma integral_on_upper_left{c:ℂ}{t l b:ℝ}
(bc: b < c.im) (ct: c.im < t) (lc: l < c.re):
complex.I • ∫ (x: ℝ) in (c.im)..t, (l+x*complex.I-c:ℂ)⁻¹=
(complex.log (l+t*complex.I-c)-
real.log(c.re-l)-real.pi*complex.I) :=
begin
  rw sub_sub,
  have cc: (↑(real.log (c.re - l))
    + ↑real.pi * complex.I) =
    complex.log ( complex.I * (c.im)+(l - c)) :=
    by {rw ← lcre bc ct lc,
        unfold complex.log, rw← crel bc ct lc,
        simp, left, rw← lcrearg bc ct lc,},
  rw cc,
  have lhs:(λx:ℝ,(↑l + ↑x * complex.I - c)⁻¹)=
  (λx:ℝ,( complex.I * ↑x + (l - c))⁻¹) :=
    by {ext1, simp, ring_nf,}, rw lhs,
  have rhs: ↑l + ↑t * complex.I - c =
    complex.I * ↑t + (↑l - c) := by ring_nf, rw rhs,
  apply integral_of_fraction_I' ct,
  {
    intros x x_in,
    simp, simp at x_in,
    right, intro xcim,
    exact (ne_of_lt x_in.1) (zero_symm_exact xcim).symm,
  },
  {
    have func_rw: (λ (x : ℝ),
      complex.log (complex.I * ↑x + (↑l - c))) =
      (λ(z:ℂ), complex.log z)∘
      (λ(x:ℝ), (complex.I * ↑x + (↑l - c))) :=
      by {ext1,simp,}, rw func_rw,
    apply continuous_on.comp,
    {
      exact continuous_on_log_of_upper_plane,
    },
    {
      exact (affine_rtc_continuous complex.I (l-c:ℂ)).continuous_on,
    },
    {
      unfold set.maps_to, intros x x_in,
      simp, simp at x_in, split,
      exact x_in.1,
      intro idd,
      have iddre:(complex.I * ↑x + (↑l - c)).re=0:=
        (congr_arg complex.re idd).trans rfl,
      simp at iddre,
      exact (ne_of_lt lc) (zero_symm_exact iddre),
    },
  },
  {
    exact integrable_lxc_inv_cimt bc ct lc,
  },
end

lemma integral_of_reciprocal_on_left {c: ℂ}
{t l b:ℝ}(bc: b < c.im) (ct: c.im < t) (lc: l < c.re) :
contour_integral (λz:ℂ, (z-c)⁻¹) (rec_left t l b) =
complex.log (l+b*complex.I-c) -
complex.log (l+t*complex.I-c) + 2*real.pi*complex.I :=
begin
  rw [integral_left_two_pieces bc ct lc,
    integral_on_lower_left bc ct lc,
    integral_on_upper_left bc ct lc],
  simp, rw ← mul_assoc, simp, ring_nf,
end

lemma winding_number_of_rectangle {c: ℂ}
{b r t l:ℝ} (bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r):
contour_integral (λz:ℂ, (z-c)⁻¹) (rectangle b r t l)
= 2 * real.pi *complex.I :=
begin
  rw integral_along_rectangle'
    (continuous_on.mono
      (reciprocal_continuous_on c)
      (image_rectangle_sub_compl_center bc ct lc cr)),
  rw [integral_of_reciprocal_on_bottom bc lc cr,
    integral_of_reciprocal_on_top ct lc cr,
    integral_of_reciprocal_on_right bc ct cr,
    integral_of_reciprocal_on_left bc ct lc],
  ring_nf,
end

theorem Cauchy_integral_formula_rectangle{f : ℂ → E} {c: ℂ}
{b r t l:ℝ} (bc: b < c.im) (ct: c.im < t)
(lc: l < c.re) (cr: c.re < r)
(Hc : continuous_on f (set.interval l r ×ℂ set.interval b t))
(Hd : differentiable_on ℂ f (set.Ioo l r ×ℂ set.Ioo b t)):
contour_integral (λz:ℂ, (z-c)⁻¹•f(z)) (rectangle b r t l)=
(2 * real.pi *complex.I :ℂ) • f(c) :=
begin
  rw Cauchy_integral_formula_rectangle_pre bc ct lc cr Hc Hd,
  rw winding_number_of_rectangle bc ct lc cr,
end


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import category_theory.eq_to_hom
import category_theory.quotient
import combinatorics.quiver.path

/-!
# The category paths on a quiver.

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.
When `C` is a quiver, `paths C` is the category of paths.

## When the quiver is itself a category
We provide `path_composition : paths C ⥤ C`.

We check that the quotient of the path category of a category by the canonical relation
(paths are related if they compose to the same path) is equivalent to the original category.
-/

universes v₁ v₂ u₁ u₂

namespace category_theory

section

/--
A type synonym for the category of paths in a quiver.
-/
def paths (V : Type u₁) : Type u₁ := V

instance (V : Type u₁) [inhabited V] : inhabited (paths V) := ⟨(default : V)⟩

variables (V : Type u₁) [quiver.{v₁+1} V]

namespace paths

instance category_paths : category.{max u₁ v₁} (paths V) :=
{ hom := λ (X Y : V), quiver.path X Y,
  id := λ X, quiver.path.nil,
  comp := λ X Y Z f g, quiver.path.comp f g, }

variables {V}

/--
The inclusion of a quiver `V` into its path category, as a prefunctor.
-/
@[simps]
def of : V ⥤q (paths V) :=
{ obj := λ X, X,
  map := λ X Y f, f.to_path, }

local attribute [ext] functor.ext

/-- Any prefunctor from `V` lifts to a functor from `paths V` -/
def lift {C} [category C] (φ : V ⥤q C) : paths V ⥤ C :=
{ obj := φ.obj,
  map := λ X Y f, @quiver.path.rec V _ X (λ Y f, φ.obj X ⟶ φ.obj Y) (𝟙 $ φ.obj X)
                  (λ Y Z p f ihp, ihp ≫ (φ.map f)) Y f,
  map_id' := λ X, by { refl, },
  map_comp' := λ X Y Z f g, by
  { induction g with _ _ g' p ih _ _ _,
    { rw category.comp_id, refl, },
    { have : f ≫ g'.cons p = (f ≫ g').cons p, by apply quiver.path.comp_cons,
      rw this, simp only, rw [ih, category.assoc], } } }

@[simp] lemma lift_nil {C} [category C] (φ : V ⥤q C) (X : V) :
  (lift φ).map (quiver.path.nil) = 𝟙 (φ.obj X) := rfl

@[simp] lemma lift_cons {C} [category C] (φ : V ⥤q C) {X Y Z : V}
  (p : quiver.path X Y) (f : Y ⟶ Z) :
  (lift φ).map (p.cons f) = (lift φ).map p ≫ (φ.map f) := rfl

@[simp] lemma lift_to_path {C} [category C] (φ : V ⥤q C) {X Y : V} (f : X ⟶ Y) :
  (lift φ).map f.to_path = φ.map f := by {dsimp [quiver.hom.to_path,lift], simp, }

lemma lift_spec {C} [category C] (φ : V ⥤q C) :
  of ⋙q (lift φ).to_prefunctor = φ :=
begin
  apply prefunctor.ext, rotate,
  { rintro X, refl, },
  { rintro X Y f, rcases φ with ⟨φo,φm⟩,
    dsimp [lift, quiver.hom.to_path],
    simp only [category.id_comp], },
end

lemma lift_unique {C} [category C] (φ : V ⥤q C) (Φ : paths V ⥤ C)
  (hΦ : of ⋙q Φ.to_prefunctor = φ) : Φ = lift φ :=
begin
  subst_vars,
  apply functor.ext, rotate,
  { rintro X, refl, },
  { rintro X Y f,
    dsimp [lift],
    induction f with _ _ p f' ih,
    { simp only [category.comp_id], apply functor.map_id, },
    { simp only [category.comp_id, category.id_comp] at ih ⊢,
      have : Φ.map (p.cons f') = Φ.map p ≫ (Φ.map (f'.to_path)), by
      { convert functor.map_comp Φ p (f'.to_path), },
      rw [this, ih], }, },
end

/-- Two functors out of a path category are equal when they agree on singleton paths. -/
@[ext]
lemma ext_functor {C} [category C]
  {F G : paths V ⥤ C}
  (h_obj : F.obj = G.obj)
  (h : ∀ (a b : V) (e : a ⟶ b), F.map e.to_path =
  eq_to_hom (congr_fun h_obj a) ≫ G.map e.to_path ≫ eq_to_hom (congr_fun h_obj.symm b)) :
  F = G :=
begin
  ext X Y f,
  { induction f with Y' Z' g e ih,
    { erw [F.map_id, G.map_id, category.id_comp, eq_to_hom_trans, eq_to_hom_refl], },
    { erw [F.map_comp g e.to_path, G.map_comp g e.to_path, ih, h],
      simp only [category.id_comp, eq_to_hom_refl, eq_to_hom_trans_assoc, category.assoc], }, },
  { intro X, rw h_obj, }
end

end paths

variables (W : Type u₂) [quiver.{v₂+1} W]

-- A restatement of `prefunctor.map_path_comp` using `f ≫ g` instead of `f.comp g`.
@[simp] lemma prefunctor.map_path_comp' (F : V ⥤q W) {X Y Z : paths V} (f : X ⟶ Y) (g : Y ⟶ Z) :
  F.map_path (f ≫ g) = (F.map_path f).comp (F.map_path g) :=
prefunctor.map_path_comp _ _ _

end

section

variables {C : Type u₁} [category.{v₁} C]

open quiver

/-- A path in a category can be composed to a single morphism. -/
@[simp]
def compose_path {X : C} : Π {Y : C} (p : path X Y), X ⟶ Y
| _ path.nil := 𝟙 X
| _ (path.cons p e) := compose_path p ≫ e

@[simp]
lemma compose_path_to_path {X Y : C} (f : X ⟶ Y) : compose_path (f.to_path) = f :=
category.id_comp _

@[simp]
lemma compose_path_comp {X Y Z : C} (f : path X Y) (g : path Y Z) :
  compose_path (f.comp g) = compose_path f ≫ compose_path g :=
begin
  induction g with Y' Z' g e ih,
  { simp, },
  { simp [ih], },
end

@[simp]

@[simp]
lemma compose_path_comp' {X Y Z : paths C} (f : X ⟶ Y) (g : Y ⟶ Z) :
  compose_path (f ≫ g) = compose_path f ≫ compose_path g :=
compose_path_comp f g

variables (C)

/-- Composition of paths as functor from the path category of a category to the category. -/
@[simps]
def path_composition : paths C ⥤ C :=
{ obj := λ X, X,
  map := λ X Y f, compose_path f, }

/-- The canonical relation on the path category of a category:
two paths are related if they compose to the same morphism. -/
-- TODO: This, and what follows, should be generalized to
-- the `hom_rel` for the kernel of any functor.
-- Indeed, this should be part of an equivalence between congruence relations on a category `C`
-- and full, essentially surjective functors out of `C`.
@[simp]
def paths_hom_rel : hom_rel (paths C) :=
λ X Y p q, (path_composition C).map p = (path_composition C).map q

/-- The functor from a category to the canonical quotient of its path category. -/
@[simps]
def to_quotient_paths : C ⥤ quotient (paths_hom_rel C) :=
{ obj := λ X, quotient.mk X,
  map := λ X Y f, quot.mk _ f.to_path,
  map_id' := λ X, quot.sound (quotient.comp_closure.of _ _ _ (by simp)),
  map_comp' := λ X Y Z f g, quot.sound (quotient.comp_closure.of _ _ _ (by simp)), }

/-- The functor from the canonical quotient of a path category of a category
to the original category. -/
@[simps]
def quotient_paths_to : quotient (paths_hom_rel C) ⥤ C :=
quotient.lift _ (path_composition C) (λ X Y p q w, w)

/-- The canonical quotient of the path category of a category
is equivalent to the original category. -/
def quotient_paths_equiv : quotient (paths_hom_rel C) ≌ C :=
{ functor := quotient_paths_to C,
  inverse := to_quotient_paths C,
  unit_iso := nat_iso.of_components (λ X, by { cases X, refl, }) begin
    intros,
    cases X, cases Y,
    induction f,
    dsimp,
    simp only [category.comp_id, category.id_comp],
    apply quot.sound,
    apply quotient.comp_closure.of,
    simp [paths_hom_rel],
  end,
  counit_iso := nat_iso.of_components (λ X, iso.refl _) (by tidy),
  functor_unit_iso_comp' := by { intros, cases X, dsimp, simp, refl, }, }

end

end category_theory


/-
Copyright (c) 2017 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Tim Baumann, Stephen Morgan, Scott Morrison
-/
import tactic.reassoc_axiom
import category_theory.category.basic

/-!
# Functors

Defines a functor between categories, extending a `prefunctor` between quivers.

Introduces notation `C ⥤ D` for the type of all functors from `C` to `D`.
(Unfortunately the `⇒` arrow (`\functor`) is taken by core,
but in mathlib4 we should switch to this.)
-/

namespace category_theory

-- declare the `v`'s first; see `category_theory.category` for an explanation
universes v v₁ v₂ v₃ u u₁ u₂ u₃

section

set_option old_structure_cmd true

/--
`functor C D` represents a functor between categories `C` and `D`.

To apply a functor `F` to an object use `F.obj X`, and to a morphism use `F.map f`.

The axiom `map_id` expresses preservation of identities, and
`map_comp` expresses functoriality.

See <https://stacks.math.columbia.edu/tag/001B>.
-/
structure functor (C : Type u₁) [category.{v₁} C] (D : Type u₂) [category.{v₂} D]
  extends prefunctor C D : Type (max v₁ v₂ u₁ u₂) :=
(map_id'   : ∀ (X : C), map (𝟙 X) = 𝟙 (obj X) . obviously)
(map_comp' : ∀ {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z), map (f ≫ g) = (map f) ≫ (map g) . obviously)

/-- The prefunctor between the underlying quivers. -/
add_decl_doc functor.to_prefunctor

end

-- A functor is basically a function, so give ⥤ a similar precedence to → (25).
-- For example, `C × D ⥤ E` should parse as `(C × D) ⥤ E` not `C × (D ⥤ E)`.
infixr ` ⥤ `:26 := functor       -- type as \func --

restate_axiom functor.map_id'
attribute [simp] functor.map_id
restate_axiom functor.map_comp'
attribute [reassoc, simp] functor.map_comp

namespace functor

section
variables (C : Type u₁) [category.{v₁} C]

/-- `𝟭 C` is the identity functor on a category `C`. -/
protected def id : C ⥤ C :=
{ obj := λ X, X,
  map := λ _ _ f, f }

notation `𝟭` := functor.id -- Type this as `\sb1`

instance : inhabited (C ⥤ C) := ⟨functor.id C⟩

variable {C}

@[simp] lemma id_obj (X : C) : (𝟭 C).obj X = X := rfl
@[simp] lemma id_map {X Y : C} (f : X ⟶ Y) : (𝟭 C).map f = f := rfl
end

section
variables {C : Type u₁} [category.{v₁} C]
          {D : Type u₂} [category.{v₂} D]
          {E : Type u₃} [category.{v₃} E]

/--
`F ⋙ G` is the composition of a functor `F` and a functor `G` (`F` first, then `G`).
-/
def comp (F : C ⥤ D) (G : D ⥤ E) : C ⥤ E :=
{ obj := λ X, G.obj (F.obj X),
  map := λ _ _ f, G.map (F.map f) }

infixr ` ⋙ `:80 := comp

@[simp] lemma comp_obj (F : C ⥤ D) (G : D ⥤ E) (X : C) : (F ⋙ G).obj X = G.obj (F.obj X) := rfl
@[simp] lemma comp_map (F : C ⥤ D) (G : D ⥤ E) {X Y : C} (f : X ⟶ Y) :
  (F ⋙ G).map f = G.map (F.map f) := rfl

-- These are not simp lemmas because rewriting along equalities between functors
-- is not necessarily a good idea.
-- Natural isomorphisms are also provided in `whiskering.lean`.
protected lemma comp_id (F : C ⥤ D) : F ⋙ (𝟭 D) = F := by cases F; refl
protected lemma id_comp (F : C ⥤ D) : (𝟭 C) ⋙ F = F := by cases F; refl

@[simp] lemma map_dite (F : C ⥤ D) {X Y : C} {P : Prop} [decidable P]
  (f : P → (X ⟶ Y)) (g : ¬P → (X ⟶ Y)) :
  F.map (if h : P then f h else g h) = if h : P then F.map (f h) else F.map (g h) :=
by { split_ifs; refl, }

end

end functor

end category_theory


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

! This file was ported from Lean 3 source module category_theory.preadditive.yoneda.basic
! leanprover-community/mathlib commit 09f981f72d43749f1fa072deade828d9c1e185bb
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Limits.Yoneda
import Mathbin.CategoryTheory.Preadditive.Opposite
import Mathbin.Algebra.Category.Module.Basic
import Mathbin.Algebra.Category.Group.Preadditive

/-!
# The Yoneda embedding for preadditive categories

The Yoneda embedding for preadditive categories sends an object `Y` to the presheaf sending an
object `X` to the group of morphisms `X ⟶ Y`. At each point, we get an additional `End Y`-module
structure.

We also show that this presheaf is additive and that it is compatible with the normal Yoneda
embedding in the expected way and deduce that the preadditive Yoneda embedding is fully faithful.

## TODO
* The Yoneda embedding is additive itself

-/

universe v u

open CategoryTheory.Preadditive Opposite CategoryTheory.Limits

noncomputable section

namespace CategoryTheory

variable {C : Type u} [Category.{v} C] [Preadditive C]

/-- The Yoneda embedding for preadditive categories sends an object `Y` to the presheaf sending an
object `X` to the `End Y`-module of morphisms `X ⟶ Y`.
-/
@[simps]
def preadditiveYonedaObj (Y : C) : Cᵒᵖ ⥤ ModuleCat.{v} (End Y)
    where
  obj X := ModuleCat.of _ (X.unop ⟶ Y)
  map X X' f :=
    { toFun := fun g => f.unop ≫ g
      map_add' := fun g g' => comp_add _ _ _ _ _ _
      map_smul' := fun r g => Eq.symm <| Category.assoc _ _ _ }
#align category_theory.preadditive_yoneda_obj CategoryTheory.preadditiveYonedaObj

/-- The Yoneda embedding for preadditive categories sends an object `Y` to the presheaf sending an
object `X` to the group of morphisms `X ⟶ Y`. At each point, we get an additional `End Y`-module
structure, see `preadditive_yoneda_obj`.
-/
@[simps]
def preadditiveYoneda : C ⥤ Cᵒᵖ ⥤ AddCommGroupCat.{v}
    where
  obj Y := preadditiveYonedaObj Y ⋙ forget₂ _ _
  map Y Y' f :=
    { app := fun X =>
        { toFun := fun g => g ≫ f
          map_zero' := Limits.zero_comp
          map_add' := fun g g' => add_comp _ _ _ _ _ _ }
      naturality' := fun X X' g => AddCommGroupCat.ext _ _ _ _ fun x => Category.assoc _ _ _ }
  map_id' X := by
    ext
    simp
  map_comp' X Y Z f g := by
    ext
    simp
#align category_theory.preadditive_yoneda CategoryTheory.preadditiveYoneda

/-- The Yoneda embedding for preadditive categories sends an object `X` to the copresheaf sending an
object `Y` to the `End X`-module of morphisms `X ⟶ Y`.
-/
@[simps]
def preadditiveCoyonedaObj (X : Cᵒᵖ) : C ⥤ ModuleCat.{v} (End X)
    where
  obj Y := ModuleCat.of _ (unop X ⟶ Y)
  map Y Y' f :=
    { toFun := fun g => g ≫ f
      map_add' := fun g g' => add_comp _ _ _ _ _ _
      map_smul' := fun r g => Category.assoc _ _ _ }
#align category_theory.preadditive_coyoneda_obj CategoryTheory.preadditiveCoyonedaObj

/-- The Yoneda embedding for preadditive categories sends an object `X` to the copresheaf sending an
object `Y` to the group of morphisms `X ⟶ Y`. At each point, we get an additional `End X`-module
structure, see `preadditive_coyoneda_obj`.
-/
@[simps]
def preadditiveCoyoneda : Cᵒᵖ ⥤ C ⥤ AddCommGroupCat.{v}
    where
  obj X := preadditiveCoyonedaObj X ⋙ forget₂ _ _
  map X X' f :=
    { app := fun Y =>
        { toFun := fun g => f.unop ≫ g
          map_zero' := Limits.comp_zero
          map_add' := fun g g' => comp_add _ _ _ _ _ _ }
      naturality' := fun Y Y' g =>
        AddCommGroupCat.ext _ _ _ _ fun x => Eq.symm <| Category.assoc _ _ _ }
  map_id' X := by
    ext
    simp
  map_comp' X Y Z f g := by
    ext
    simp
#align category_theory.preadditive_coyoneda CategoryTheory.preadditiveCoyoneda

instance additive_yoneda_obj (X : C) : Functor.Additive (preadditiveYonedaObj X) where
#align category_theory.additive_yoneda_obj CategoryTheory.additive_yoneda_obj

instance additive_yoneda_obj' (X : C) : Functor.Additive (preadditiveYoneda.obj X) where
#align category_theory.additive_yoneda_obj' CategoryTheory.additive_yoneda_obj'

instance additive_coyoneda_obj (X : Cᵒᵖ) : Functor.Additive (preadditiveCoyonedaObj X) where
#align category_theory.additive_coyoneda_obj CategoryTheory.additive_coyoneda_obj

instance additive_coyoneda_obj' (X : Cᵒᵖ) : Functor.Additive (preadditiveCoyoneda.obj X) where
#align category_theory.additive_coyoneda_obj' CategoryTheory.additive_coyoneda_obj'

/-- Composing the preadditive yoneda embedding with the forgetful functor yields the regular
Yoneda embedding.
-/
@[simp]
theorem whiskering_preadditiveYoneda :
    preadditiveYoneda ⋙
        (whiskeringRight Cᵒᵖ AddCommGroupCat (Type v)).obj (forget AddCommGroupCat) =
      yoneda :=
  rfl
#align category_theory.whiskering_preadditive_yoneda CategoryTheory.whiskering_preadditiveYoneda

/-- Composing the preadditive yoneda embedding with the forgetful functor yields the regular
Yoneda embedding.
-/
@[simp]
theorem whiskering_preadditiveCoyoneda :
    preadditiveCoyoneda ⋙
        (whiskeringRight C AddCommGroupCat (Type v)).obj (forget AddCommGroupCat) =
      coyoneda :=
  rfl
#align category_theory.whiskering_preadditive_coyoneda CategoryTheory.whiskering_preadditiveCoyoneda

instance preadditiveYonedaFull : Full (preadditiveYoneda : C ⥤ Cᵒᵖ ⥤ AddCommGroupCat) :=
  let yoneda_full :
    Full
      (preadditiveYoneda ⋙
        (whiskeringRight Cᵒᵖ AddCommGroupCat (Type v)).obj (forget AddCommGroupCat)) :=
    Yoneda.yonedaFull
  full.of_comp_faithful preadditive_yoneda
    ((whiskering_right Cᵒᵖ AddCommGroupCat (Type v)).obj (forget AddCommGroupCat))
#align category_theory.preadditive_yoneda_full CategoryTheory.preadditiveYonedaFull

instance preadditiveCoyonedaFull : Full (preadditiveCoyoneda : Cᵒᵖ ⥤ C ⥤ AddCommGroupCat) :=
  let coyoneda_full :
    Full
      (preadditiveCoyoneda ⋙
        (whiskeringRight C AddCommGroupCat (Type v)).obj (forget AddCommGroupCat)) :=
    Coyoneda.coyonedaFull
  full.of_comp_faithful preadditive_coyoneda
    ((whiskering_right C AddCommGroupCat (Type v)).obj (forget AddCommGroupCat))
#align category_theory.preadditive_coyoneda_full CategoryTheory.preadditiveCoyonedaFull

instance preadditiveYoneda_faithful : Faithful (preadditiveYoneda : C ⥤ Cᵒᵖ ⥤ AddCommGroupCat) :=
  Faithful.of_comp_eq whiskering_preadditiveYoneda
#align category_theory.preadditive_yoneda_faithful CategoryTheory.preadditiveYoneda_faithful

instance preadditiveCoyoneda_faithful :
    Faithful (preadditiveCoyoneda : Cᵒᵖ ⥤ C ⥤ AddCommGroupCat) :=
  Faithful.of_comp_eq whiskering_preadditiveCoyoneda
#align category_theory.preadditive_coyoneda_faithful CategoryTheory.preadditiveCoyoneda_faithful

end CategoryTheory