/-
Copyright (c) 2022 Felix Weilacher. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Felix Weilacher
-/
import topology.separation
import topology.bases

/-!
# Perfect Sets

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

In this file we define perfect subsets of a topological space, and prove some basic properties,
including a version of the Cantor-Bendixson Theorem.

## Main Definitions

* `perfect C`: A set `C` is perfect, meaning it is closed and every point of it
  is an accumulation point of itself.

## Main Statements

* `perfect.splitting`: A perfect nonempty set contains two disjoint perfect nonempty subsets.
  The main inductive step in the construction of an embedding from the Cantor space to a
  perfect nonempty complete metric space.
* `exists_countable_union_perfect_of_is_closed`: One version of the **Cantor-Bendixson Theorem**:
  A closed set in a second countable space can be written as the union of a countable set and a
  perfect set.

## Implementation Notes

We do not require perfect sets to be nonempty.

We define a nonstandard predicate, `preperfect`, which drops the closed-ness requirement
from the definition of perfect. In T1 spaces, this is equivalent to having a perfect closure,
see `preperfect_iff_perfect_closure`.

## References

* [kechris1995] (Chapter 6)

## Tags

accumulation point, perfect set, Cantor-Bendixson.

-/

open_locale topology filter
open topological_space filter set

variables {α : Type*} [topological_space α] {C : set α}

/-- If `x` is an accumulation point of a set `C` and `U` is a neighborhood of `x`,
then `x` is an accumulation point of `U ∩ C`. -/
theorem acc_pt.nhds_inter {x : α} {U : set α} (h_acc : acc_pt x (𝓟 C)) (hU : U ∈ 𝓝 x) :
  acc_pt x (𝓟 (U ∩ C)) :=
begin
  have : 𝓝[≠] x ≤ 𝓟 U,
  { rw le_principal_iff,
    exact mem_nhds_within_of_mem_nhds hU, },
  rw [acc_pt, ← inf_principal, ← inf_assoc, inf_of_le_left this],
  exact h_acc,
end

/-- A set `C` is preperfect if all of its points are accumulation points of itself.
If `C` is nonempty and `α` is a T1 space, this is equivalent to the closure of `C` being perfect.
See `preperfect_iff_perfect_closure`.-/
def preperfect (C : set α) : Prop := ∀ x ∈ C, acc_pt x (𝓟 C)

/-- A set `C` is called perfect if it is closed and all of its
points are accumulation points of itself.
Note that we do not require `C` to be nonempty.-/
structure perfect (C : set α) : Prop :=
(closed : is_closed C)
(acc : preperfect C)

lemma preperfect_iff_nhds : preperfect C ↔ ∀ x ∈ C, ∀ U ∈ 𝓝 x, ∃ y ∈ U ∩ C, y ≠ x :=
by simp only [preperfect, acc_pt_iff_nhds]

/-- The intersection of a preperfect set and an open set is preperfect-/
theorem preperfect.open_inter {U : set α} (hC : preperfect C) (hU : is_open U) :
  preperfect (U ∩ C) :=
begin
  rintros x ⟨xU, xC⟩,
  apply (hC _ xC).nhds_inter,
  exact hU.mem_nhds xU,
end

/-- The closure of a preperfect set is perfect.
For a converse, see `preperfect_iff_perfect_closure`-/
theorem preperfect.perfect_closure (hC : preperfect C) : perfect (closure C) :=
begin
  split, { exact is_closed_closure },
  intros x hx,
  by_cases h : x ∈ C; apply acc_pt.mono _ (principal_mono.mpr subset_closure),
  { exact hC _ h },
  have : {x}ᶜ ∩ C = C := by simp [h],
  rw [acc_pt, nhds_within, inf_assoc, inf_principal, this],
  rw [closure_eq_cluster_pts] at hx,
  exact hx,
end

/-- In a T1 space, being preperfect is equivalent to having perfect closure.-/
theorem preperfect_iff_perfect_closure [t1_space α] :
  preperfect C ↔ perfect (closure C) :=
begin
  split; intro h, { exact h.perfect_closure },
  intros x xC,
  have H : acc_pt x (𝓟 (closure C)) := h.acc _ (subset_closure xC),
  rw acc_pt_iff_frequently at *,
  have : ∀ y , y ≠ x ∧ y ∈ closure C → ∃ᶠ z in 𝓝 y, z ≠ x ∧ z ∈ C,
  { rintros y ⟨hyx, yC⟩,
    simp only [← mem_compl_singleton_iff, @and_comm _ (_ ∈ C) , ← frequently_nhds_within_iff,
      hyx.nhds_within_compl_singleton, ← mem_closure_iff_frequently],
    exact yC, },
  rw ← frequently_frequently_nhds,
  exact H.mono this,
end

theorem perfect.closure_nhds_inter {U : set α} (hC : perfect C) (x : α) (xC : x ∈ C) (xU : x ∈ U)
  (Uop : is_open U) : perfect (closure (U ∩ C)) ∧ (closure (U ∩ C)).nonempty :=
begin
  split,
  { apply preperfect.perfect_closure,
    exact (hC.acc).open_inter Uop, },
  apply nonempty.closure,
  exact ⟨x, ⟨xU, xC⟩⟩,
end

/-- Given a perfect nonempty set in a T2.5 space, we can find two disjoint perfect subsets
This is the main inductive step in the proof of the Cantor-Bendixson Theorem-/
lemma perfect.splitting [t2_5_space α] (hC : perfect C) (hnonempty : C.nonempty) :
  ∃ C₀ C₁ : set α, (perfect C₀ ∧ C₀.nonempty ∧ C₀ ⊆ C) ∧
  (perfect C₁ ∧ C₁.nonempty ∧ C₁ ⊆ C) ∧ disjoint C₀ C₁ :=
begin
  cases hnonempty with y yC,
  obtain ⟨x, xC, hxy⟩ : ∃ x ∈ C, x ≠ y,
  { have := hC.acc _ yC,
    rw acc_pt_iff_nhds at this,
    rcases this univ (univ_mem) with ⟨x, xC, hxy⟩,
    exact ⟨x, xC.2, hxy⟩, },
  obtain ⟨U, xU, Uop, V, yV, Vop, hUV⟩ := exists_open_nhds_disjoint_closure hxy,
  use [closure (U ∩ C), closure (V ∩ C)],
  split; rw ← and_assoc,
  { refine ⟨hC.closure_nhds_inter x xC xU Uop, _⟩,
    rw hC.closed.closure_subset_iff,
    exact inter_subset_right _ _, },
  split,
  { refine ⟨hC.closure_nhds_inter y yC yV Vop, _⟩,
    rw hC.closed.closure_subset_iff,
    exact inter_subset_right _ _, },
  apply disjoint.mono _ _ hUV; apply closure_mono; exact inter_subset_left _ _,
end

section kernel

/-- The **Cantor-Bendixson Theorem**: Any closed subset of a second countable space
can be written as the union of a countable set and a perfect set.-/
theorem exists_countable_union_perfect_of_is_closed [second_countable_topology α]
  (hclosed : is_closed C) :
  ∃ V D : set α, (V.countable) ∧ (perfect D) ∧ (C = V ∪ D) :=
begin
  obtain ⟨b, bct, bnontrivial, bbasis⟩ := topological_space.exists_countable_basis α,
  let v := {U ∈ b | (U ∩ C).countable},
  let V := ⋃ U ∈ v, U,
  let D := C \ V,
  have Vct : (V ∩ C).countable,
  { simp only [Union_inter, mem_sep_iff],
    apply countable.bUnion,
    { exact countable.mono (inter_subset_left _ _) bct, },
    { exact inter_subset_right _ _, }, },
  refine ⟨V ∩ C, D, Vct, ⟨_, _⟩, _⟩,
  { refine hclosed.sdiff (is_open_bUnion (λ U, _)),
    exact λ ⟨Ub, _⟩, is_topological_basis.is_open bbasis Ub, },
  { rw preperfect_iff_nhds,
    intros x xD E xE,
    have : ¬ (E ∩ D).countable,
    { intro h,
      obtain ⟨U, hUb, xU, hU⟩ : ∃ U ∈ b, x ∈ U ∧ U ⊆ E,
      { exact (is_topological_basis.mem_nhds_iff bbasis).mp xE, },
      have hU_cnt : (U ∩ C).countable,
      { apply @countable.mono _ _ ((E ∩ D) ∪ (V ∩ C)),
        { rintros y ⟨yU, yC⟩,
          by_cases y ∈ V,
          { exact mem_union_right _ (mem_inter h yC), },
          { exact mem_union_left _ (mem_inter (hU yU) ⟨yC, h⟩), }, },
        exact countable.union h Vct, },
      have : U ∈ v := ⟨hUb, hU_cnt⟩,
      apply xD.2,
      exact mem_bUnion this xU, },
    by_contradiction h,
    push_neg at h,
    exact absurd (countable.mono h (set.countable_singleton _)) this, },
  { rw [inter_comm, inter_union_diff], },
end

/-- Any uncountable closed set in a second countable space contains a nonempty perfect subset.-/
theorem exists_perfect_nonempty_of_is_closed_of_not_countable [second_countable_topology α]
  (hclosed : is_closed C) (hunc : ¬ C.countable) :
  ∃ D : set α, perfect D ∧ D.nonempty ∧ D ⊆ C :=
begin
  rcases exists_countable_union_perfect_of_is_closed hclosed with ⟨V, D, Vct, Dperf, VD⟩,
  refine ⟨D, ⟨Dperf, _⟩⟩,
  split,
  { rw nonempty_iff_ne_empty,
    by_contradiction,
    rw [h, union_empty] at VD,
    rw VD at hunc,
    contradiction, },
  rw VD,
  exact subset_union_right _ _,
end

end kernel


-- Macro for the `syntax` category
macro "many " x:stx : stx => `(stx| ($x)*)

syntax "sum! " (many term:max) : term

macro_rules
| `(sum! $xs*) => do
  let mut r ← `(0)
  for x in xs do
    r ← `($r + $x)
  return r

#check sum! 1 2 3
#eval sum! 1 2 3
#check sum!

theorem ex : sum! 1 2 3 = 6 :=
rfl


theorem ex1 (x : Nat) (y : { v // v > x }) (z : Nat) : Nat :=
by {
  clear y x;
  exact z
}

theorem ex2 (x : Nat) (y : { v // v > x }) (z : Nat) : Nat :=
by {
  clear x y;
  exact z
}

theorem ex3 (x y z : Nat) (h₁ : x = y) (h₂ : z = y) : x = z :=
by {
  have : y = z := h₂.symm;
  apply Eq.trans;
  exact h₁;
  assumption
}

theorem ex4 (x y z : Nat) (h₁ : x = y) (h₂ : z = y) : x = z :=
by {
  let h₃ : y = z := h₂.symm;
  apply Eq.trans;
  exact h₁;
  exact h₃
}

theorem ex5 (x y z : Nat) (h₁ : x = y) (h₂ : z = y) : x = z :=
by {
  have h₃ : y = z := h₂.symm;
  apply Eq.trans;
  exact h₁;
  exact h₃
}

theorem ex6 (x y z : Nat) (h₁ : x = y) (h₂ : z = y) : id (x + 0 = z) :=
by {
  show x = z;
  have h₃ : y = z := h₂.symm;
  apply Eq.trans;
  exact h₁;
  exact h₃
}

theorem ex7 (x y z : Nat) (h₁ : x = y) (h₂ : z = y) : x = z := by
have : y = z := by apply Eq.symm; assumption
apply Eq.trans
exact h₁
assumption

theorem ex8 (x y z : Nat) (h₁ : x = y) (h₂ : z = y) : x = z :=
by apply Eq.trans h₁;
   have : y = z := by
     apply Eq.symm;
     assumption;
   exact this

example (x y z : Nat) (h₁ : x = y) (h₂ : z = y) : x = z := by
  sorry

example (x y z : Nat) (h₁ : x = y) (h₂ : z = y) : x = z := by
  apply Eq.trans
  . sorry
  . sorry
  . sorry

example (x y z : Nat) (h₁ : x = y) (h₂ : z = y) : x = z := by
  apply Eq.trans <;> sorry


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

! This file was ported from Lean 3 source module data.dfinsupp.multiset
! leanprover-community/mathlib commit 740acc0e6f9adf4423f92a485d0456fc271482da
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Dfinsupp.Order

/-!
# Equivalence between `Multiset` and `ℕ`-valued finitely supported functions

This defines `Dfinsupp.toMultiset` the equivalence between `Π₀ a : α, ℕ` and `Multiset α`, along
with `Multiset.toDfinsupp` the reverse equivalence.

Note that this provides a computable alternative to `Finsupp.toMultiset`.
-/

variable {α : Type _} {β : α → Type _}

namespace Dfinsupp

/-- Non-dependent special case of `Dfinsupp.addZeroClass` to help typeclass search. -/
instance addZeroClass' {β} [AddZeroClass β] : AddZeroClass (Π₀ _a : α, β) :=
  @Dfinsupp.addZeroClass α (fun _ ↦ β) _
#align dfinsupp.add_zero_class' Dfinsupp.addZeroClass'

variable [DecidableEq α]

/-- A computable version of `Finsupp.toMultiset`. -/
def toMultiset : (Π₀ _a : α, ℕ) →+ Multiset α :=
  Dfinsupp.sumAddHom fun a : α ↦ Multiset.replicateAddMonoidHom a
#align dfinsupp.to_multiset Dfinsupp.toMultiset

@[simp]
theorem toMultiset_single (a : α) (n : ℕ) :
    toMultiset (Dfinsupp.single a n) = Multiset.replicate n a :=
  Dfinsupp.sumAddHom_single _ _ _
#align dfinsupp.to_multiset_single Dfinsupp.toMultiset_single

end Dfinsupp

namespace Multiset

variable [DecidableEq α]

/-- A computable version of `Multiset.toFinsupp`. -/
def toDfinsupp : Multiset α →+ Π₀ _a : α, ℕ where
  toFun s :=
    { toFun := fun n ↦ s.count n
      support' := Trunc.mk ⟨s, fun i ↦ (em (i ∈ s)).imp_right Multiset.count_eq_zero_of_not_mem⟩ }
  map_zero' := rfl
  map_add' _ _ := Dfinsupp.ext fun _ ↦ Multiset.count_add _ _ _
#align multiset.to_dfinsupp Multiset.toDfinsupp

@[simp]
theorem toDfinsupp_apply (s : Multiset α) (a : α) : Multiset.toDfinsupp s a = s.count a :=
  rfl
#align multiset.to_dfinsupp_apply Multiset.toDfinsupp_apply

@[simp]
theorem toDfinsupp_support (s : Multiset α) : s.toDfinsupp.support = s.toFinset :=
  (Finset.filter_eq_self _).mpr fun _ hx ↦ count_ne_zero.mpr <| Multiset.mem_toFinset.1 hx
#align multiset.to_dfinsupp_support Multiset.toDfinsupp_support

@[simp]
theorem toDfinsupp_replicate (a : α) (n : ℕ) :
    toDfinsupp (Multiset.replicate n a) = Dfinsupp.single a n := by
  ext i
  dsimp [toDfinsupp]
  simp [count_replicate, eq_comm]
#align multiset.to_dfinsupp_replicate Multiset.toDfinsupp_replicate

@[simp]
theorem toDfinsupp_singleton (a : α) : toDfinsupp {a} = Dfinsupp.single a 1 := by
  rw [← replicate_one, toDfinsupp_replicate]
#align multiset.to_dfinsupp_singleton Multiset.toDfinsupp_singleton

/-- `Multiset.toDfinsupp` as an `AddEquiv`. -/
@[simps! apply symm_apply]
def equivDfinsupp : Multiset α ≃+ Π₀ _a : α, ℕ :=
  AddMonoidHom.toAddEquiv Multiset.toDfinsupp Dfinsupp.toMultiset
    (by
      -- Porting note: used to be ext.
      /- potential bug: in lean 3, `trace.ext` outputs
         "matched goal to rule: dfinsupp.add_hom_ext'"
         but that lemma does not apply!-/
      apply Multiset.addHom_ext
      simp)
    (by
      -- Porting note: used to be ext
      apply Dfinsupp.addHom_ext
      simp)
#align multiset.equiv_dfinsupp Multiset.equivDfinsupp

@[simp]
theorem toDfinsupp_toMultiset (s : Multiset α) : Dfinsupp.toMultiset (Multiset.toDfinsupp s) = s :=
  equivDfinsupp.symm_apply_apply s
#align multiset.to_dfinsupp_to_multiset Multiset.toDfinsupp_toMultiset

@[simp]
theorem toDfinsupp_le_toDfinsupp (s t : Multiset α) : toDfinsupp s ≤ toDfinsupp t ↔ s ≤ t := by
  simp [Multiset.le_iff_count, Dfinsupp.le_def]
#align multiset.to_dfinsupp_le_to_dfinsupp Multiset.toDfinsupp_le_toDfinsupp

end Multiset

@[simp]
theorem Dfinsupp.toMultiset_toDfinsupp [DecidableEq α] (f : Π₀ _a : α, ℕ) :
    Multiset.toDfinsupp (Dfinsupp.toMultiset f) = f :=
  Multiset.equivDfinsupp.apply_symm_apply f
#align dfinsupp.to_multiset_to_dfinsupp Dfinsupp.toMultiset_toDfinsupp


/-
Copyright (c) 2022 Stuart Presnell. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Stuart Presnell, Eric Wieser, Yaël Dillies, Patrick Massot, Scott Morrison
-/
import algebra.group_power.order
import algebra.ring.regular

/-!
# Algebraic instances for unit intervals

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

For suitably structured underlying type `α`, we exhibit the structure of
the unit intervals (`set.Icc`, `set.Ioc`, `set.Ioc`, and `set.Ioo`) from `0` to `1`.

Note: Instances for the interval `Ici 0` are dealt with in `algebra/order/nonneg.lean`.

## Main definitions
The strongest typeclass provided on each interval is:
* `set.Icc.cancel_comm_monoid_with_zero`
* `set.Ico.comm_semigroup`
* `set.Ioc.comm_monoid`
* `set.Ioo.comm_semigroup`

## TODO
* algebraic instances for intervals -1 to 1
* algebraic instances for `Ici 1`
* algebraic instances for `(Ioo (-1) 1)ᶜ`
* provide `has_distrib_neg` instances where applicable
* prove versions of `mul_le_{left,right}` for other intervals
* prove versions of the lemmas in `topology/unit_interval` with `ℝ` generalized to
  some arbitrary ordered semiring

-/

open set

variables {α : Type*}

section ordered_semiring
variables [ordered_semiring α]

/-! ### Instances for `↥(set.Icc 0 1)` -/

namespace set.Icc

instance has_zero : has_zero (Icc (0:α) 1) := { zero := ⟨0, left_mem_Icc.2 zero_le_one⟩ }

instance has_one : has_one (Icc (0:α) 1) := { one := ⟨1, right_mem_Icc.2 zero_le_one⟩ }

@[simp, norm_cast] lemma coe_zero : ↑(0 : Icc (0:α) 1) = (0 : α) := rfl
@[simp, norm_cast]

@[simp] lemma mk_zero (h : (0 : α) ∈ Icc (0 : α) 1) : (⟨0, h⟩ : Icc (0:α) 1) = 0 := rfl
@[simp] lemma mk_one (h : (1 : α) ∈ Icc (0 : α) 1) : (⟨1, h⟩ : Icc (0:α) 1) = 1 := rfl

@[simp, norm_cast] lemma coe_eq_zero {x : Icc (0:α) 1} : (x : α) = 0 ↔ x = 0 :=
by { symmetry, exact subtype.ext_iff }

lemma coe_ne_zero {x : Icc (0:α) 1} : (x : α) ≠ 0 ↔ x ≠ 0 :=
not_iff_not.mpr coe_eq_zero

@[simp, norm_cast] lemma coe_eq_one {x : Icc (0:α) 1} : (x : α) = 1 ↔ x = 1 :=
by { symmetry, exact subtype.ext_iff }

lemma coe_ne_one {x : Icc (0:α) 1} : (x : α) ≠ 1 ↔ x ≠ 1 :=
not_iff_not.mpr coe_eq_one

lemma coe_nonneg (x : Icc (0:α) 1) : 0 ≤ (x : α) := x.2.1
lemma coe_le_one (x : Icc (0:α) 1) : (x : α) ≤ 1 := x.2.2

/-- like `coe_nonneg`, but with the inequality in `Icc (0:α) 1`. -/
lemma nonneg {t : Icc (0:α) 1} : 0 ≤ t := t.2.1
/-- like `coe_le_one`, but with the inequality in `Icc (0:α) 1`. -/
lemma le_one {t : Icc (0:α) 1} : t ≤ 1 := t.2.2

instance has_mul : has_mul (Icc (0:α) 1) :=
{ mul := λ p q, ⟨p*q, ⟨mul_nonneg p.2.1 q.2.1, mul_le_one p.2.2 q.2.1 q.2.2⟩⟩ }

instance has_pow : has_pow (Icc (0:α) 1) ℕ :=
{ pow := λ p n, ⟨p.1 ^ n, ⟨pow_nonneg p.2.1 n, pow_le_one n p.2.1 p.2.2⟩⟩ }

@[simp, norm_cast] lemma coe_mul (x y : Icc (0:α) 1) : ↑(x * y) = (x * y : α) := rfl
@[simp, norm_cast] lemma coe_pow (x : Icc (0:α) 1) (n : ℕ) : ↑(x ^ n) = (x ^ n : α) := rfl

lemma mul_le_left {x y : Icc (0:α) 1} : x * y ≤ x :=
(mul_le_mul_of_nonneg_left y.2.2 x.2.1).trans_eq (mul_one x)

lemma mul_le_right {x y : Icc (0:α) 1} : x * y ≤ y :=
(mul_le_mul_of_nonneg_right x.2.2 y.2.1).trans_eq (one_mul y)

instance monoid_with_zero : monoid_with_zero (Icc (0:α) 1) :=
subtype.coe_injective.monoid_with_zero _ coe_zero coe_one coe_mul coe_pow

instance comm_monoid_with_zero {α : Type*} [ordered_comm_semiring α] :
  comm_monoid_with_zero (Icc (0:α) 1) :=
subtype.coe_injective.comm_monoid_with_zero _ coe_zero coe_one coe_mul coe_pow

instance cancel_monoid_with_zero {α : Type*} [ordered_ring α] [no_zero_divisors α] :
  cancel_monoid_with_zero (Icc (0:α) 1) :=
@function.injective.cancel_monoid_with_zero α _ no_zero_divisors.to_cancel_monoid_with_zero
  _ _ _ _ coe subtype.coe_injective coe_zero coe_one coe_mul coe_pow

instance cancel_comm_monoid_with_zero {α : Type*} [ordered_comm_ring α] [no_zero_divisors α] :
  cancel_comm_monoid_with_zero (Icc (0:α) 1) :=
@function.injective.cancel_comm_monoid_with_zero α _
  no_zero_divisors.to_cancel_comm_monoid_with_zero
  _ _ _ _ coe subtype.coe_injective coe_zero coe_one coe_mul coe_pow

variables {β : Type*} [ordered_ring β]

lemma one_sub_mem {t : β} (ht : t ∈ Icc (0:β) 1) : 1 - t ∈ Icc (0:β) 1 :=
by { rw mem_Icc at *, exact ⟨sub_nonneg.2 ht.2, (sub_le_self_iff _).2 ht.1⟩ }

lemma mem_iff_one_sub_mem {t : β} : t ∈ Icc (0:β) 1 ↔ 1 - t ∈ Icc (0:β) 1 :=
⟨one_sub_mem, λ h, (sub_sub_cancel 1 t) ▸ one_sub_mem h⟩

lemma one_sub_nonneg (x : Icc (0:β) 1) : 0 ≤ 1 - (x : β) := by simpa using x.2.2
lemma one_sub_le_one (x : Icc (0:β) 1) : 1 - (x : β) ≤ 1 := by simpa using x.2.1

end set.Icc

/-! ### Instances for `↥(set.Ico 0 1)` -/

namespace set.Ico

instance has_zero [nontrivial α] : has_zero (Ico (0:α) 1) :=
  { zero := ⟨0, left_mem_Ico.2 zero_lt_one⟩ }

@[simp, norm_cast] lemma coe_zero [nontrivial α] : ↑(0 : Ico (0:α) 1) = (0 : α) := rfl

@[simp] lemma mk_zero [nontrivial α] (h : (0 : α) ∈ Ico (0 : α) 1) : (⟨0, h⟩ : Ico (0:α) 1) = 0 :=
  rfl

@[simp, norm_cast] lemma coe_eq_zero [nontrivial α] {x : Ico (0:α) 1} : (x : α) = 0 ↔ x = 0 :=
by { symmetry, exact subtype.ext_iff }

lemma coe_ne_zero [nontrivial α] {x : Ico (0:α) 1} : (x : α) ≠ 0 ↔ x ≠ 0 :=
not_iff_not.mpr coe_eq_zero

lemma coe_nonneg (x : Ico (0:α) 1) : 0 ≤ (x : α) := x.2.1
lemma coe_lt_one (x : Ico (0:α) 1) : (x : α) < 1 := x.2.2

/-- like `coe_nonneg`, but with the inequality in `Ico (0:α) 1`. -/
lemma nonneg [nontrivial α] {t : Ico (0:α) 1} : 0 ≤ t := t.2.1

instance has_mul : has_mul (Ico (0:α) 1) :=
{ mul := λ p q, ⟨p*q, ⟨mul_nonneg p.2.1 q.2.1,
  mul_lt_one_of_nonneg_of_lt_one_right p.2.2.le q.2.1 q.2.2⟩⟩ }

@[simp, norm_cast] lemma coe_mul (x y : Ico (0:α) 1) : ↑(x * y) = (x * y : α) := rfl

instance semigroup : semigroup (Ico (0:α) 1) :=
subtype.coe_injective.semigroup _ coe_mul

instance comm_semigroup {α : Type*} [ordered_comm_semiring α] : comm_semigroup (Ico (0:α) 1) :=
subtype.coe_injective.comm_semigroup _ coe_mul

end set.Ico

end ordered_semiring

variables [strict_ordered_semiring α]

/-! ### Instances for `↥(set.Ioc 0 1)` -/

namespace set.Ioc

instance has_one [nontrivial α] : has_one (Ioc (0:α) 1) := { one := ⟨1, ⟨zero_lt_one, le_refl 1⟩⟩ }

@[simp, norm_cast] lemma coe_one [nontrivial α] : ↑(1 : Ioc (0:α) 1) = (1 : α) := rfl

@[simp] lemma mk_one [nontrivial α] (h : (1 : α) ∈ Ioc (0 : α) 1) : (⟨1, h⟩ : Ioc (0:α) 1) = 1 :=
  rfl

@[simp, norm_cast] lemma coe_eq_one [nontrivial α] {x : Ioc (0:α) 1} : (x : α) = 1 ↔ x = 1 :=
by { symmetry, exact subtype.ext_iff }

lemma coe_ne_one [nontrivial α] {x : Ioc (0:α) 1} : (x : α) ≠ 1 ↔ x ≠ 1 :=
not_iff_not.mpr coe_eq_one

lemma coe_pos (x : Ioc (0:α) 1) : 0 < (x : α) := x.2.1
lemma coe_le_one (x : Ioc (0:α) 1) : (x : α) ≤ 1 := x.2.2

/-- like `coe_le_one`, but with the inequality in `Ioc (0:α) 1`. -/
lemma le_one [nontrivial α] {t : Ioc (0:α) 1} : t ≤ 1 := t.2.2

instance has_mul : has_mul (Ioc (0:α) 1) :=
{ mul := λ p q, ⟨p.1 * q.1, ⟨mul_pos p.2.1 q.2.1, mul_le_one p.2.2 (le_of_lt q.2.1) q.2.2⟩⟩ }

instance has_pow : has_pow (Ioc (0:α) 1) ℕ :=
{ pow := λ p n, ⟨p.1 ^ n, ⟨pow_pos p.2.1 n, pow_le_one n (le_of_lt p.2.1) p.2.2⟩⟩ }

@[simp, norm_cast] lemma coe_mul (x y : Ioc (0:α) 1) : ↑(x * y) = (x * y : α) := rfl
@[simp, norm_cast] lemma coe_pow (x : Ioc (0:α) 1) (n : ℕ) : ↑(x ^ n) = (x ^ n : α) := rfl

instance semigroup : semigroup (Ioc (0:α) 1) :=
subtype.coe_injective.semigroup _ coe_mul

instance monoid [nontrivial α] : monoid (Ioc (0:α) 1) :=
subtype.coe_injective.monoid _ coe_one coe_mul coe_pow

instance comm_semigroup {α : Type*} [strict_ordered_comm_semiring α] :
  comm_semigroup (Ioc (0:α) 1) :=
subtype.coe_injective.comm_semigroup _ coe_mul

instance comm_monoid {α : Type*} [strict_ordered_comm_semiring α] [nontrivial α] :
  comm_monoid (Ioc (0:α) 1) :=
subtype.coe_injective.comm_monoid _ coe_one coe_mul coe_pow

instance cancel_monoid {α : Type*} [strict_ordered_ring α] [is_domain α] :
  cancel_monoid (Ioc (0:α) 1) :=
{ mul_left_cancel := λ a b c h,
    subtype.ext $ mul_left_cancel₀ a.prop.1.ne' $ (congr_arg subtype.val h : _),
  mul_right_cancel := λ a b c h,
    subtype.ext $ mul_right_cancel₀ b.prop.1.ne' $ (congr_arg subtype.val h : _),
  ..set.Ioc.monoid}

instance cancel_comm_monoid {α : Type*} [strict_ordered_comm_ring α] [is_domain α] :
  cancel_comm_monoid (Ioc (0:α) 1) :=
{ ..set.Ioc.cancel_monoid, ..set.Ioc.comm_monoid }

end set.Ioc

/-! ### Instances for `↥(set.Ioo 0 1)` -/

namespace set.Ioo

lemma pos (x : Ioo (0:α) 1) : 0 < (x : α) := x.2.1
lemma lt_one (x : Ioo (0:α) 1) : (x : α) < 1 := x.2.2

instance has_mul : has_mul (Ioo (0:α) 1) := { mul := λ p q, ⟨p.1 * q.1, ⟨mul_pos p.2.1 q.2.1,
  mul_lt_one_of_nonneg_of_lt_one_right p.2.2.le q.2.1.le q.2.2⟩⟩ }

@[simp, norm_cast] lemma coe_mul (x y : Ioo (0:α) 1) : ↑(x * y) = (x * y : α) := rfl

instance semigroup : semigroup (Ioo (0:α) 1) :=
subtype.coe_injective.semigroup _ coe_mul

instance comm_semigroup {α : Type*} [strict_ordered_comm_semiring α] :
  comm_semigroup (Ioo (0:α) 1) :=
subtype.coe_injective.comm_semigroup _ coe_mul

variables {β : Type*} [ordered_ring β]

lemma one_sub_mem {t : β} (ht : t ∈ Ioo (0:β) 1) : 1 - t ∈ Ioo (0:β) 1 :=
begin
  rw mem_Ioo at *,
  refine ⟨sub_pos.2 ht.2, _⟩,
  exact lt_of_le_of_ne ((sub_le_self_iff 1).2 ht.1.le) (mt sub_eq_self.mp ht.1.ne'),
end

lemma mem_iff_one_sub_mem {t : β} : t ∈ Ioo (0:β) 1 ↔ 1 - t ∈ Ioo (0:β) 1 :=
⟨one_sub_mem, λ h, (sub_sub_cancel 1 t) ▸ one_sub_mem h⟩

lemma one_minus_pos (x : Ioo (0:β) 1) : 0 < 1 - (x : β) := by simpa using x.2.2
lemma one_minus_lt_one (x : Ioo (0:β) 1) : 1 - (x : β) < 1 := by simpa using x.2.1

end set.Ioo


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import algebra.homology.additive
import tactic.abel

/-!
# Chain homotopies

We define chain homotopies, and prove that homotopic chain maps induce the same map on homology.
-/

universes v u

open_locale classical
noncomputable theory

open category_theory category_theory.limits homological_complex

variables {ι : Type*}
variables {V : Type u} [category.{v} V] [preadditive V]

variables {c : complex_shape ι} {C D E : homological_complex V c}
variables (f g : C ⟶ D) (h k : D ⟶ E) (i : ι)

section

/-- The composition of `C.d i i' ≫ f i' i` if there is some `i'` coming after `i`,
and `0` otherwise. -/
def d_next (i : ι) : (Π i j, C.X i ⟶ D.X j) →+ (C.X i ⟶ D.X i) :=
add_monoid_hom.mk' (λ f, match c.next i with
| none := 0
| some ⟨i',w⟩ := C.d i i' ≫ f i' i
end)
begin
  intros f g,
  rcases c.next i with _|⟨i',w⟩,
  exact (zero_add _).symm,
  exact preadditive.comp_add _ _ _ _ _ _,
end

/-- `f i' i` if `i'` comes after `i`, and 0 if there's no such `i'`.
Hopefully there won't be much need for this, except in `d_next_eq_d_from_from_next`
to see that `d_next` factors through `C.d_from i`. -/
def from_next [has_zero_object V] (i : ι) : (Π i j, C.X i ⟶ D.X j) →+ (C.X_next i ⟶ D.X i) :=
add_monoid_hom.mk' (λ f, match c.next i with
| none := 0
| some ⟨i',w⟩ := (C.X_next_iso w).hom ≫ f i' i
end)
begin
  intros f g,
  rcases c.next i with _|⟨i',w⟩,
  exact (zero_add _).symm,
  exact preadditive.comp_add _ _ _ _ _ _,
end

@[simp]
lemma d_next_eq_d_from_from_next [has_zero_object V] (f : Π i j, C.X i ⟶ D.X j) (i : ι) :
  d_next i f = C.d_from i ≫ from_next i f :=
begin
  dsimp [d_next, from_next],
  rcases c.next i with ⟨⟩|⟨⟨i', w⟩⟩;
  { dsimp [d_next, from_next], simp },
end

lemma d_next_eq (f : Π i j, C.X i ⟶ D.X j) {i i' : ι} (w : c.rel i i') :
  d_next i f = C.d i i' ≫ f i' i :=
begin
  dsimp [d_next],
  rw c.next_eq_some w,
  refl,
end

@[simp] lemma d_next_comp_left (f : C ⟶ D) (g : Π i j, D.X i ⟶ E.X j) (i : ι) :
  d_next i (λ i j, f.f i ≫ g i j) = f.f i ≫ d_next i g :=
begin
  dsimp [d_next],
  rcases c.next i with _|⟨i',w⟩,
  { exact comp_zero.symm, },
  { dsimp [d_next],
    simp, },
end

@[simp] lemma d_next_comp_right (f : Π i j, C.X i ⟶ D.X j) (g : D ⟶ E) (i : ι) :
  d_next i (λ i j, f i j ≫ g.f j) = d_next i f ≫ g.f i :=
begin
  dsimp [d_next],
  rcases c.next i with _|⟨i',w⟩,
  { exact zero_comp.symm, },
  { dsimp [d_next],
    simp, },
end

/-- The composition of `f j j' ≫ D.d j' j` if there is some `j'` coming before `j`,
and `0` otherwise. -/
def prev_d (j : ι) : (Π i j, C.X i ⟶ D.X j) →+ (C.X j ⟶ D.X j) :=
add_monoid_hom.mk' (λ f, match c.prev j with
| none := 0
| some ⟨j',w⟩ := f j j' ≫ D.d j' j
end)
begin
  intros f g,
  rcases c.prev j with _|⟨j',w⟩,
  exact (zero_add _).symm,
  exact preadditive.add_comp _ _ _ _ _ _,
end

/-- `f j j'` if `j'` comes after `j`, and 0 if there's no such `j'`.
Hopefully there won't be much need for this, except in `d_next_eq_d_from_from_next`
to see that `d_next` factors through `C.d_from i`. -/
def to_prev [has_zero_object V] (j : ι) : (Π i j, C.X i ⟶ D.X j) →+ (C.X j ⟶ D.X_prev j) :=
add_monoid_hom.mk' (λ f, match c.prev j with
| none := 0
| some ⟨j',w⟩ := f j j' ≫ (D.X_prev_iso w).inv
end)
begin
  intros f g,
  rcases c.prev j with _|⟨j',w⟩,
  exact (zero_add _).symm,
  exact preadditive.add_comp _ _ _ _ _ _,
end

@[simp]
lemma prev_d_eq_to_prev_d_to [has_zero_object V] (f : Π i j, C.X i ⟶ D.X j) (j : ι) :
  prev_d j f = to_prev j f ≫ D.d_to j :=
begin
  dsimp [prev_d, to_prev],
  rcases c.prev j with ⟨⟩|⟨⟨j', w⟩⟩;
  { dsimp [prev_d, to_prev], simp },
end

lemma prev_d_eq (f : Π i j, C.X i ⟶ D.X j) {j j' : ι} (w : c.rel j' j) :
  prev_d j f = f j j' ≫ D.d j' j :=
begin
  dsimp [prev_d],
  rw c.prev_eq_some w,
  refl,
end

@[simp] lemma prev_d_comp_left (f : C ⟶ D) (g : Π i j, D.X i ⟶ E.X j) (j : ι) :
  prev_d j (λ i j, f.f i ≫ g i j) = f.f j ≫ prev_d j g :=
begin
  dsimp [prev_d],
  rcases c.prev j with _|⟨j',w⟩,
  { exact comp_zero.symm, },
  { dsimp [prev_d, hom.prev],
    simp, },
end

@[simp] lemma prev_d_comp_right (f : Π i j, C.X i ⟶ D.X j) (g : D ⟶ E) (j : ι) :
  prev_d j (λ i j, f i j ≫ g.f j) = prev_d j f ≫ g.f j :=
begin
  dsimp [prev_d],
  rcases c.prev j with _|⟨j',w⟩,
  { exact zero_comp.symm, },
  { dsimp [prev_d],
    simp, },
end

lemma d_next_nat (C D : chain_complex V ℕ) (i : ℕ) (f : Π i j, C.X i ⟶ D.X j) :
  d_next i f = C.d i (i-1) ≫ f (i-1) i :=
begin
  cases i,
  { dsimp [d_next],
    rcases (complex_shape.down ℕ).next 0 with _|⟨j,hj⟩;
    dsimp [d_next],
    { rw [C.shape, zero_comp], dsimp, dec_trivial },
    { dsimp at hj, exact (nat.succ_ne_zero _ hj).elim } },
  rw d_next_eq, dsimp, refl
end

lemma prev_d_nat (C D : cochain_complex V ℕ) (i : ℕ) (f : Π i j, C.X i ⟶ D.X j) :
  prev_d i f = f i (i-1) ≫ D.d (i-1) i :=
begin
  cases i,
  { dsimp [prev_d],
    rcases (complex_shape.up ℕ).prev 0 with _|⟨j,hj⟩;
    dsimp [prev_d],
    { rw [D.shape, comp_zero], dsimp, dec_trivial },
    { dsimp at hj, exact (nat.succ_ne_zero _ hj).elim } },
  rw prev_d_eq, dsimp, refl
end

/--
A homotopy `h` between chain maps `f` and `g` consists of components `h i j : C.X i ⟶ D.X j`
which are zero unless `c.rel j i`, satisfying the homotopy condition.
-/
@[ext, nolint has_inhabited_instance]
structure homotopy (f g : C ⟶ D) :=
(hom : Π i j, C.X i ⟶ D.X j)
(zero' : ∀ i j, ¬ c.rel j i → hom i j = 0 . obviously)
(comm : ∀ i, f.f i = d_next i hom + prev_d i hom + g.f i . obviously')

variables {f g}
namespace homotopy

restate_axiom homotopy.zero'

/--
`f` is homotopic to `g` iff `f - g` is homotopic to `0`.
-/
def equiv_sub_zero : homotopy f g ≃ homotopy (f - g) 0 :=
{ to_fun := λ h,
  { hom := λ i j, h.hom i j,
    zero' := λ i j w, h.zero _ _ w,
    comm := λ i, by simp [h.comm] },
  inv_fun := λ h,
  { hom := λ i j, h.hom i j,
    zero' := λ i j w, h.zero _ _ w,
    comm := λ i, by simpa [sub_eq_iff_eq_add] using h.comm i },
  left_inv := by tidy,
  right_inv := by tidy, }

/-- Equal chain maps are homotopic. -/
@[simps]
def of_eq (h : f = g) : homotopy f g :=
{ hom := 0,
  zero' := λ _ _ _, rfl,
  comm := λ _, by simp only [add_monoid_hom.map_zero, zero_add, h] }

/-- Every chain map is homotopic to itself. -/
@[simps, refl]
def refl (f : C ⟶ D) : homotopy f f :=
of_eq (rfl : f = f)

/-- `f` is homotopic to `g` iff `g` is homotopic to `f`. -/
@[simps, symm]
def symm {f g : C ⟶ D} (h : homotopy f g) : homotopy g f :=
{ hom := -h.hom,
  zero' := λ i j w, by rw [pi.neg_apply, pi.neg_apply, h.zero i j w, neg_zero],
  comm := λ i, by rw [add_monoid_hom.map_neg, add_monoid_hom.map_neg, h.comm, ← neg_add,
      ← add_assoc, neg_add_self, zero_add] }

/-- homotopy is a transitive relation. -/
@[simps, trans]
def trans {e f g : C ⟶ D} (h : homotopy e f) (k : homotopy f g) : homotopy e g :=
{ hom := h.hom + k.hom,
  zero' := λ i j w, by rw [pi.add_apply, pi.add_apply, h.zero i j w, k.zero i j w, zero_add],
  comm := λ i, by { rw [add_monoid_hom.map_add, add_monoid_hom.map_add, h.comm, k.comm], abel }, }

/-- the sum of two homotopies is a homotopy between the sum of the respective morphisms. -/
@[simps]
def add {f₁ g₁ f₂ g₂ : C ⟶ D}
  (h₁ : homotopy f₁ g₁) (h₂ : homotopy f₂ g₂) : homotopy (f₁+f₂) (g₁+g₂) :=
{ hom := h₁.hom + h₂.hom,
  zero' := λ i j hij, by
    rw [pi.add_apply, pi.add_apply, h₁.zero' i j hij, h₂.zero' i j hij, add_zero],
  comm := λ i, by
    { simp only [homological_complex.add_f_apply, h₁.comm, h₂.comm,
        add_monoid_hom.map_add],
      abel, }, }

/-- homotopy is closed under composition (on the right) -/
@[simps]
def comp_right {e f : C ⟶ D} (h : homotopy e f) (g : D ⟶ E) : homotopy (e ≫ g) (f ≫ g) :=
{ hom := λ i j, h.hom i j ≫ g.f j,
  zero' := λ i j w, by rw [h.zero i j w, zero_comp],
  comm := λ i, by simp only [h.comm i, d_next_comp_right, preadditive.add_comp,
    prev_d_comp_right, comp_f], }

/-- homotopy is closed under composition (on the left) -/
@[simps]
def comp_left {f g : D ⟶ E} (h : homotopy f g) (e : C ⟶ D) : homotopy (e ≫ f) (e ≫ g) :=
{ hom := λ i j, e.f i ≫ h.hom i j,
  zero' := λ i j w, by rw [h.zero i j w, comp_zero],
  comm := λ i, by simp only [h.comm i, d_next_comp_left, preadditive.comp_add,
    prev_d_comp_left, comp_f], }

/-- homotopy is closed under composition -/
@[simps]
def comp {C₁ C₂ C₃ : homological_complex V c} {f₁ g₁ : C₁ ⟶ C₂} {f₂ g₂ : C₂ ⟶ C₃}
  (h₁ : homotopy f₁ g₁) (h₂ : homotopy f₂ g₂) : homotopy (f₁ ≫ f₂) (g₁ ≫ g₂) :=
(h₁.comp_right _).trans (h₂.comp_left _)

/-- a variant of `homotopy.comp_right` useful for dealing with homotopy equivalences. -/
@[simps]
def comp_right_id {f : C ⟶ C} (h : homotopy f (𝟙 C)) (g : C ⟶ D) : homotopy (f ≫ g) g :=
(h.comp_right g).trans (of_eq $ category.id_comp _)

/-- a variant of `homotopy.comp_left` useful for dealing with homotopy equivalences. -/
@[simps]
def comp_left_id {f : D ⟶ D} (h : homotopy f (𝟙 D)) (g : C ⟶ D) : homotopy (g ≫ f) g :=
(h.comp_left g).trans (of_eq $ category.comp_id _)

/-!
Null homotopic maps can be constructed using the formula `hd+dh`. We show that
these morphisms are homotopic to `0` and provide some convenient simplification
lemmas that give a degreewise description of `hd+dh`, depending on whether we have
two differentials going to and from a certain degree, only one, or none.
-/

/-- The null homotopic map associated to a family `hom` of morphisms `C_i ⟶ D_j`.
This is the same datum as for the field `hom` in the structure `homotopy`. For
this definition, we do not need the field `zero` of that structure
as this definition uses only the maps `C_i ⟶ C_j` when `c.rel j i`. -/
def null_homotopic_map (hom : Π i j, C.X i ⟶ D.X j) : C ⟶ D :=
{ f      := λ i, d_next i hom + prev_d i hom,
  comm'  := λ i j hij,
  begin
    have eq1 : prev_d i hom ≫ D.d i j = 0,
    { rcases h : c.prev i with _|⟨i',w⟩,
      { dsimp [prev_d], rw h, erw zero_comp, },
      { rw [prev_d_eq hom w, category.assoc, D.d_comp_d' i' i j w hij, comp_zero], }, },
    have eq2 : C.d i j ≫ d_next j hom = 0,
    { rcases h : c.next j with _|⟨j',w⟩,
      { dsimp [d_next], rw h, erw comp_zero, },
      { rw [d_next_eq hom w, ← category.assoc, C.d_comp_d' i j j' hij w, zero_comp], }, },
    rw [d_next_eq hom hij, prev_d_eq hom hij, preadditive.comp_add, preadditive.add_comp,
      eq1, eq2, add_zero, zero_add, category.assoc],
  end }

/-- Variant of `null_homotopic_map` where the input consists only of the
relevant maps `C_i ⟶ D_j` such that `c.rel j i`. -/
def null_homotopic_map' (h : Π i j, c.rel j i → (C.X i ⟶ D.X j)) : C ⟶ D :=
null_homotopic_map (λ i j, dite (c.rel j i) (h i j) (λ _, 0))

/-- Compatibility of `null_homotopic_map` with the postcomposition by a morphism
of complexes. -/
lemma null_homotopic_map_comp (hom : Π i j, C.X i ⟶ D.X j) (g : D ⟶ E) :
null_homotopic_map hom ≫ g = null_homotopic_map (λ i j, hom i j ≫ g.f j) :=
begin
  ext n,
  dsimp [null_homotopic_map],
  simp only [preadditive.add_comp, d_next_comp_right, prev_d_comp_right],
end

/-- Compatibility of `null_homotopic_map'` with the postcomposition by a morphism
of complexes. -/
lemma null_homotopic_map'_comp (hom : Π i j, c.rel j i → (C.X i ⟶ D.X j)) (g : D ⟶ E) :
null_homotopic_map' hom ≫ g = null_homotopic_map' (λ i j hij, hom i j hij ≫ g.f j) :=
begin
  ext n,
  erw null_homotopic_map_comp,
  congr',
  ext i j,
  split_ifs,
  { refl, },
  { rw zero_comp, },
end

/-- Compatibility of `null_homotopic_map` with the precomposition by a morphism
of complexes. -/
lemma comp_null_homotopic_map (f : C ⟶ D) (hom : Π i j, D.X i ⟶ E.X j) :
f ≫ null_homotopic_map hom = null_homotopic_map (λ i j, f.f i ≫ hom i j) :=
begin
  ext n,
  dsimp [null_homotopic_map],
  simp only [preadditive.comp_add, d_next_comp_left, prev_d_comp_left],
end

/-- Compatibility of `null_homotopic_map'` with the precomposition by a morphism
of complexes. -/
lemma comp_null_homotopic_map' (f : C ⟶ D) (hom : Π i j, c.rel j i → (D.X i ⟶ E.X j)) :
f ≫ null_homotopic_map' hom = null_homotopic_map' (λ i j hij, f.f i ≫ hom i j hij) :=
begin
  ext n,
  erw comp_null_homotopic_map,
  congr',
  ext i j,
  split_ifs,
  { refl, },
  { rw comp_zero, },
end

/-- Compatibility of `null_homotopic_map` with the application of additive functors -/
lemma map_null_homotopic_map {W : Type*} [category W] [preadditive W]
  (G : V ⥤ W) [G.additive] (hom : Π i j, C.X i ⟶ D.X j) :
  (G.map_homological_complex c).map (null_homotopic_map hom) =
  null_homotopic_map (λ i j, G.map (hom i j)) :=
begin
  ext i,
  dsimp [null_homotopic_map, d_next, prev_d],
  rcases c.next i with _|⟨inext,wn⟩;
  rcases c.prev i with _|⟨iprev,wp⟩;
  dsimp [d_next, prev_d];
  simp only [G.map_comp, functor.map_zero, functor.map_add],
end

/-- Compatibility of `null_homotopic_map'` with the application of additive functors -/
lemma map_null_homotopic_map' {W : Type*} [category W] [preadditive W]
  (G : V ⥤ W) [G.additive] (hom : Π i j, c.rel j i → (C.X i ⟶ D.X j)) :
  (G.map_homological_complex c).map (null_homotopic_map' hom) =
  null_homotopic_map' (λ i j hij, G.map (hom i j hij)) :=
begin
  ext n,
  erw map_null_homotopic_map,
  congr',
  ext i j,
  split_ifs,
  { refl, },
  { rw G.map_zero, }
end

/-- Tautological construction of the `homotopy` to zero for maps constructed by
`null_homotopic_map`, at least when we have the `zero'` condition. -/
@[simps]
def null_homotopy (hom : Π i j, C.X i ⟶ D.X j) (zero' : ∀ i j, ¬ c.rel j i → hom i j = 0) :
  homotopy (null_homotopic_map hom) 0 :=
{ hom := hom,
  zero' := zero',
  comm := by { intro i, rw [homological_complex.zero_f_apply, add_zero], refl, }, }

/-- Homotopy to zero for maps constructed with `null_homotopic_map'` -/
@[simps]
def null_homotopy' (h : Π i j, c.rel j i → (C.X i ⟶ D.X j)) :
  homotopy (null_homotopic_map' h) 0 :=
begin
  apply null_homotopy (λ i j, dite (c.rel j i) (h i j) (λ _, 0)),
  intros i j hij,
  dsimp,
  rw [dite_eq_right_iff],
  intro hij',
  exfalso,
  exact hij hij',
end

/-! This lemma and the following ones can be used in order to compute
the degreewise morphisms induced by the null homotopic maps constructed
with `null_homotopic_map` or `null_homotopic_map'` -/
@[simp]
lemma null_homotopic_map_f {k₂ k₁ k₀ : ι} (r₂₁ : c.rel k₂ k₁) (r₁₀ : c.rel k₁ k₀)
  (hom : Π i j, C.X i ⟶ D.X j) :
  (null_homotopic_map hom).f k₁ = C.d k₁ k₀ ≫ hom k₀ k₁ + hom k₁ k₂ ≫ D.d k₂ k₁ :=
by { dsimp [null_homotopic_map], rw [d_next_eq hom r₁₀, prev_d_eq hom r₂₁], }

@[simp]
lemma null_homotopic_map'_f {k₂ k₁ k₀  : ι} (r₂₁ : c.rel k₂ k₁) (r₁₀ : c.rel k₁ k₀)
  (h : Π i j, c.rel j i → (C.X i ⟶ D.X j)) :
  (null_homotopic_map' h).f k₁ = C.d k₁ k₀ ≫ h k₀ k₁ r₁₀ + h k₁ k₂ r₂₁ ≫ D.d k₂ k₁ :=
begin
  simp only [← null_homotopic_map'],
  rw null_homotopic_map_f r₂₁ r₁₀ (λ i j, dite (c.rel j i) (h i j) (λ _, 0)),
  dsimp,
  split_ifs,
  refl,
end

@[simp]
lemma null_homotopic_map_f_of_not_rel_left {k₁ k₀ : ι} (r₁₀ : c.rel k₁ k₀)
  (hk₀ : ∀ l : ι, ¬c.rel k₀ l)
  (hom : Π i j, C.X i ⟶ D.X j) :
  (null_homotopic_map hom).f k₀ = hom k₀ k₁ ≫ D.d k₁ k₀ :=
begin
  dsimp [null_homotopic_map],
  rw prev_d_eq hom r₁₀,
  rcases h : c.next k₀ with _|⟨l,w⟩, swap, exfalso, exact hk₀ l w,
  dsimp [d_next], rw h, erw zero_add,
end

@[simp]
lemma null_homotopic_map'_f_of_not_rel_left {k₁ k₀ : ι} (r₁₀ : c.rel k₁ k₀)
  (hk₀ : ∀ l : ι, ¬c.rel k₀ l)
  (h : Π i j, c.rel j i → (C.X i ⟶ D.X j)) :
  (null_homotopic_map' h).f k₀ = h k₀ k₁ r₁₀ ≫ D.d k₁ k₀ :=
begin
  simp only [← null_homotopic_map'],
  rw null_homotopic_map_f_of_not_rel_left r₁₀ hk₀ (λ i j, dite (c.rel j i) (h i j) (λ _, 0)),
  dsimp,
  split_ifs,
  refl,
end

@[simp]
lemma null_homotopic_map_f_of_not_rel_right {k₁ k₀ : ι} (r₁₀ : c.rel k₁ k₀)
  (hk₁ : ∀ l : ι, ¬c.rel l k₁)
  (hom : Π i j, C.X i ⟶ D.X j) :
  (null_homotopic_map hom).f k₁ = C.d k₁ k₀ ≫ hom k₀ k₁ :=
begin
  dsimp [null_homotopic_map],
  rw d_next_eq hom r₁₀,
  rcases h : c.prev k₁ with _|⟨l,w⟩, swap, exfalso, exact hk₁ l w,
  dsimp [prev_d], rw h, erw add_zero,
end

@[simp]
lemma null_homotopic_map'_f_of_not_rel_right {k₁ k₀ : ι} (r₁₀ : c.rel k₁ k₀)
  (hk₁ : ∀ l : ι, ¬c.rel l k₁)
  (h : Π i j, c.rel j i → (C.X i ⟶ D.X j)) :
  (null_homotopic_map' h).f k₁ = C.d k₁ k₀ ≫ h k₀ k₁ r₁₀ :=
begin
  simp only [← null_homotopic_map'],
  rw null_homotopic_map_f_of_not_rel_right r₁₀ hk₁ (λ i j, dite (c.rel j i) (h i j) (λ _, 0)),
  dsimp,
  split_ifs,
  refl,
end

@[simp]
lemma null_homotopic_map_f_eq_zero {k₀ : ι}
  (hk₀ : ∀ l : ι, ¬c.rel k₀ l) (hk₀' : ∀ l : ι, ¬c.rel l k₀)
  (hom : Π i j, C.X i ⟶ D.X j) :
  (null_homotopic_map hom).f k₀ = 0 :=
begin
  dsimp [null_homotopic_map],
  rcases h1 : c.next k₀ with _|⟨l,w⟩, swap, exfalso, exact hk₀ l w,
  rcases h2 : c.prev k₀ with _|⟨l,w⟩, swap, exfalso, exact hk₀' l w,
  dsimp [d_next, prev_d],
  rw [h1, h2],
  erw zero_add,
  refl,
end

@[simp]
lemma null_homotopic_map'_f_eq_zero {k₀ : ι}
  (hk₀ : ∀ l : ι, ¬c.rel k₀ l) (hk₀' : ∀ l : ι, ¬c.rel l k₀)
  (h : Π i j, c.rel j i → (C.X i ⟶ D.X j)) :
  (null_homotopic_map' h).f k₀ = 0 :=
begin
  simp only [← null_homotopic_map'],
  exact null_homotopic_map_f_eq_zero hk₀ hk₀'
    (λ i j, dite (c.rel j i) (h i j) (λ _, 0)),
end

/-!
`homotopy.mk_inductive` allows us to build a homotopy of chain complexes inductively,
so that as we construct each component, we have available the previous two components,
and the fact that they satisfy the homotopy condition.

To simplify the situation, we only construct homotopies of the form `homotopy e 0`.
`homotopy.equiv_sub_zero` can provide the general case.

Notice however, that this construction does not have particularly good definitional properties:
we have to insert `eq_to_hom` in several places.
Hopefully this is okay in most applications, where we only need to have the existence of some
homotopy.
-/
section mk_inductive

variables {P Q : chain_complex V ℕ}

@[simp] lemma prev_d_chain_complex (f : Π i j, P.X i ⟶ Q.X j) (j : ℕ) :
  prev_d j f = f j (j+1) ≫ Q.d _ _ :=
begin
  dsimp [prev_d],
  simp only [chain_complex.prev],
  refl,
end

@[simp] lemma d_next_succ_chain_complex (f : Π i j, P.X i ⟶ Q.X j) (i : ℕ) :
  d_next (i+1) f = P.d _ _ ≫ f i (i+1) :=
begin
  dsimp [d_next],
  simp only [chain_complex.next_nat_succ],
  refl,
end

@[simp] lemma d_next_zero_chain_complex (f : Π i j, P.X i ⟶ Q.X j) :
  d_next 0 f = 0 :=
begin
  dsimp [d_next],
  simp only [chain_complex.next_nat_zero],
  refl,
end

variables (e : P ⟶ Q)
  (zero : P.X 0 ⟶ Q.X 1)
  (comm_zero : e.f 0 = zero ≫ Q.d 1 0)
  (one : P.X 1 ⟶ Q.X 2)
  (comm_one : e.f 1 = P.d 1 0 ≫ zero + one ≫ Q.d 2 1)
  (succ : ∀ (n : ℕ)
    (p : Σ' (f : P.X n ⟶ Q.X (n+1)) (f' : P.X (n+1) ⟶ Q.X (n+2)),
      e.f (n+1) = P.d (n+1) n ≫ f + f' ≫ Q.d (n+2) (n+1)),
    Σ' f'' : P.X (n+2) ⟶ Q.X (n+3), e.f (n+2) = P.d (n+2) (n+1) ≫ p.2.1 + f'' ≫ Q.d (n+3) (n+2))

include comm_one comm_zero

/--
An auxiliary construction for `mk_inductive`.

Here we build by induction a family of diagrams,
but don't require at the type level that these successive diagrams actually agree.
They do in fact agree, and we then capture that at the type level (i.e. by constructing a homotopy)
in `mk_inductive`.

At this stage, we don't check the homotopy condition in degree 0,
because it "falls off the end", and is easier to treat using `X_next` and `X_prev`,
which we do in `mk_inductive_aux₂`.
-/
@[simp, nolint unused_arguments]
def mk_inductive_aux₁ :
  Π n, Σ' (f : P.X n ⟶ Q.X (n+1)) (f' : P.X (n+1) ⟶ Q.X (n+2)),
    e.f (n+1) = P.d (n+1) n ≫ f + f' ≫ Q.d (n+2) (n+1)
| 0 := ⟨zero, one, comm_one⟩
| 1 := ⟨one, (succ 0 ⟨zero, one, comm_one⟩).1, (succ 0 ⟨zero, one, comm_one⟩).2⟩
| (n+2) :=
  ⟨(mk_inductive_aux₁ (n+1)).2.1,
    (succ (n+1) (mk_inductive_aux₁ (n+1))).1,
    (succ (n+1) (mk_inductive_aux₁ (n+1))).2⟩

section

variable [has_zero_object V]

/--
An auxiliary construction for `mk_inductive`.
-/
@[simp]
def mk_inductive_aux₂ :
  Π n, Σ' (f : P.X_next n ⟶ Q.X n) (f' : P.X n ⟶ Q.X_prev n), e.f n = P.d_from n ≫ f + f' ≫ Q.d_to n
| 0 := ⟨0, zero ≫ (Q.X_prev_iso rfl).inv, by simpa using comm_zero⟩
| (n+1) := let I := mk_inductive_aux₁ e zero comm_zero one comm_one succ n in
  ⟨(P.X_next_iso rfl).hom ≫ I.1, I.2.1 ≫ (Q.X_prev_iso rfl).inv, by simpa using I.2.2⟩

lemma mk_inductive_aux₃ (i : ℕ) :
  (mk_inductive_aux₂ e zero comm_zero one comm_one succ i).2.1 ≫ (Q.X_prev_iso rfl).hom
    = (P.X_next_iso rfl).inv ≫ (mk_inductive_aux₂ e zero comm_zero one comm_one succ (i+1)).1 :=
by rcases i with (_|_|i); { dsimp, simp, }

/--
A constructor for a `homotopy e 0`, for `e` a chain map between `ℕ`-indexed chain complexes,
working by induction.

You need to provide the components of the homotopy in degrees 0 and 1,
show that these satisfy the homotopy condition,
and then give a construction of each component,
and the fact that it satisfies the homotopy condition,
using as an inductive hypothesis the data and homotopy condition for the previous two components.
-/
def mk_inductive : homotopy e 0 :=
{ hom := λ i j, if h : i + 1 = j then
    (mk_inductive_aux₂ e zero comm_zero one comm_one succ i).2.1 ≫ (Q.X_prev_iso h).hom
  else
    0,
  zero' := λ i j w, by rwa dif_neg,
  comm := λ i, begin
    dsimp, simp only [add_zero],
    convert (mk_inductive_aux₂ e zero comm_zero one comm_one succ i).2.2,
    { rcases i with (_|_|_|i),
      { dsimp,
        simp only [d_next_zero_chain_complex, d_from_eq_zero, limits.comp_zero], },
      all_goals
      { simp only [d_next_succ_chain_complex],
        dsimp,
        simp only [category.comp_id, category.assoc, iso.inv_hom_id, d_from_comp_X_next_iso_assoc,
          dite_eq_ite, if_true, eq_self_iff_true]}, },
    { cases i,
      all_goals
      { simp only [prev_d_chain_complex],
        dsimp,
        simp only [category.comp_id, category.assoc, iso.inv_hom_id, X_prev_iso_comp_d_to,
          dite_eq_ite, if_true, eq_self_iff_true], }, },
  end, }

end

end mk_inductive

/-!
`homotopy.mk_coinductive` allows us to build a homotopy of cochain complexes inductively,
so that as we construct each component, we have available the previous two components,
and the fact that they satisfy the homotopy condition.
-/
section mk_coinductive

variables {P Q : cochain_complex V ℕ}

@[simp] lemma d_next_cochain_complex (f : Π i j, P.X i ⟶ Q.X j) (j : ℕ) :
  d_next j f = P.d _ _ ≫ f (j+1) j :=
begin
  dsimp [d_next],
  simp only [cochain_complex.next],
  refl,
end

@[simp] lemma prev_d_succ_cochain_complex (f : Π i j, P.X i ⟶ Q.X j) (i : ℕ) :
  prev_d (i+1) f = f (i+1) _ ≫ Q.d i (i+1) :=
begin
  dsimp [prev_d],
  simp [cochain_complex.prev_nat_succ],
  refl,
end

@[simp] lemma prev_d_zero_cochain_complex (f : Π i j, P.X i ⟶ Q.X j) :
  prev_d 0 f = 0 :=
begin
  dsimp [prev_d],
  simp only [cochain_complex.prev_nat_zero],
  refl,
end

variables (e : P ⟶ Q)
  (zero : P.X 1 ⟶ Q.X 0)
  (comm_zero : e.f 0 = P.d 0 1 ≫ zero)
  (one : P.X 2 ⟶ Q.X 1)
  (comm_one : e.f 1 = zero ≫ Q.d 0 1 + P.d 1 2 ≫ one)
  (succ : ∀ (n : ℕ)
    (p : Σ' (f : P.X (n+1) ⟶ Q.X n) (f' : P.X (n+2) ⟶ Q.X (n+1)),
      e.f (n+1) = f ≫ Q.d n (n+1) + P.d (n+1) (n+2) ≫ f'),
    Σ' f'' : P.X (n+3) ⟶ Q.X (n+2), e.f (n+2) = p.2.1 ≫ Q.d (n+1) (n+2) + P.d (n+2) (n+3) ≫ f'')

include comm_one comm_zero succ

/--
An auxiliary construction for `mk_coinductive`.

Here we build by induction a family of diagrams,
but don't require at the type level that these successive diagrams actually agree.
They do in fact agree, and we then capture that at the type level (i.e. by constructing a homotopy)
in `mk_coinductive`.

At this stage, we don't check the homotopy condition in degree 0,
because it "falls off the end", and is easier to treat using `X_next` and `X_prev`,
which we do in `mk_inductive_aux₂`.
-/
@[simp, nolint unused_arguments]
def mk_coinductive_aux₁ :
  Π n, Σ' (f : P.X (n+1) ⟶ Q.X n) (f' : P.X (n+2) ⟶ Q.X (n+1)),
    e.f (n+1) = f ≫ Q.d n (n+1) + P.d (n+1) (n+2) ≫ f'
| 0 := ⟨zero, one, comm_one⟩
| 1 := ⟨one, (succ 0 ⟨zero, one, comm_one⟩).1, (succ 0 ⟨zero, one, comm_one⟩).2⟩
| (n+2) :=
  ⟨(mk_coinductive_aux₁ (n+1)).2.1,
    (succ (n+1) (mk_coinductive_aux₁ (n+1))).1,
    (succ (n+1) (mk_coinductive_aux₁ (n+1))).2⟩

section

variable [has_zero_object V]

/--
An auxiliary construction for `mk_inductive`.
-/
@[simp]
def mk_coinductive_aux₂ :
  Π n, Σ' (f : P.X n ⟶ Q.X_prev n) (f' : P.X_next n ⟶ Q.X n),
    e.f n = f ≫ Q.d_to n + P.d_from n ≫ f'
| 0 := ⟨0, (P.X_next_iso rfl).hom ≫ zero, by simpa using comm_zero⟩
| (n+1) := let I := mk_coinductive_aux₁ e zero comm_zero one comm_one succ n in
  ⟨I.1 ≫ (Q.X_prev_iso rfl).inv, (P.X_next_iso rfl).hom ≫ I.2.1, by simpa using I.2.2⟩

lemma mk_coinductive_aux₃ (i : ℕ) :
  (mk_coinductive_aux₂ e zero comm_zero one comm_one succ i).2.1 ≫ (Q.X_prev_iso rfl).inv
    = (P.X_next_iso rfl).hom ≫ (mk_coinductive_aux₂ e zero comm_zero one comm_one succ (i+1)).1 :=
by rcases i with (_|_|i); { dsimp, simp, }

/--
A constructor for a `homotopy e 0`, for `e` a chain map between `ℕ`-indexed cochain complexes,
working by induction.

You need to provide the components of the homotopy in degrees 0 and 1,
show that these satisfy the homotopy condition,
and then give a construction of each component,
and the fact that it satisfies the homotopy condition,
using as an inductive hypothesis the data and homotopy condition for the previous two components.
-/
def mk_coinductive : homotopy e 0 :=
{ hom := λ i j, if h : j + 1 = i then
    (P.X_next_iso h).inv ≫ (mk_coinductive_aux₂ e zero comm_zero one comm_one succ j).2.1
  else
    0,
  zero' := λ i j w, by rwa dif_neg,
  comm := λ i, begin
    dsimp,
    rw [add_zero, add_comm],
    convert (mk_coinductive_aux₂ e zero comm_zero one comm_one succ i).2.2 using 2,
    { rcases i with (_|_|_|i),
      { simp only [mk_coinductive_aux₂, prev_d_zero_cochain_complex, zero_comp] },
      all_goals
      { simp only [prev_d_succ_cochain_complex],
        dsimp,
        simp only [eq_self_iff_true, iso.inv_hom_id_assoc, dite_eq_ite,
          if_true, category.assoc, X_prev_iso_comp_d_to], }, },
    { cases i,
      { dsimp,
        simp only [eq_self_iff_true, d_next_cochain_complex, dif_pos,
          d_from_comp_X_next_iso_assoc, ←comm_zero],
      rw mk_coinductive_aux₂,
      dsimp,
      convert comm_zero.symm,
      simp only [iso.inv_hom_id_assoc], },
      { dsimp,
        simp only [eq_self_iff_true, d_next_cochain_complex, dif_pos, d_from_comp_X_next_iso_assoc],
        rw mk_coinductive_aux₂,
        dsimp only,
        simp only [iso.inv_hom_id_assoc], }, },
  end }

end

end mk_coinductive

end homotopy

/--
A homotopy equivalence between two chain complexes consists of a chain map each way,
and homotopies from the compositions to the identity chain maps.

Note that this contains data;
arguably it might be more useful for many applications if we truncated it to a Prop.
-/
structure homotopy_equiv (C D : homological_complex V c) :=
(hom : C ⟶ D)
(inv : D ⟶ C)
(homotopy_hom_inv_id : homotopy (hom ≫ inv) (𝟙 C))
(homotopy_inv_hom_id : homotopy (inv ≫ hom) (𝟙 D))

namespace homotopy_equiv

/-- Any complex is homotopy equivalent to itself. -/
@[refl] def refl (C : homological_complex V c) : homotopy_equiv C C :=
{ hom := 𝟙 C,
  inv := 𝟙 C,
  homotopy_hom_inv_id := by simp,
  homotopy_inv_hom_id := by simp, }

instance : inhabited (homotopy_equiv C C) := ⟨refl C⟩

/-- Being homotopy equivalent is a symmetric relation. -/
@[symm] def symm
  {C D : homological_complex V c} (f : homotopy_equiv C D) :
  homotopy_equiv D C :=
{ hom := f.inv,
  inv := f.hom,
  homotopy_hom_inv_id := f.homotopy_inv_hom_id,
  homotopy_inv_hom_id := f.homotopy_hom_inv_id, }

/-- Homotopy equivalence is a transitive relation. -/
@[trans] def trans
  {C D E : homological_complex V c} (f : homotopy_equiv C D) (g : homotopy_equiv D E) :
  homotopy_equiv C E :=
{ hom := f.hom ≫ g.hom,
  inv := g.inv ≫ f.inv,
  homotopy_hom_inv_id := by simpa using
    ((g.homotopy_hom_inv_id.comp_right_id f.inv).comp_left f.hom).trans f.homotopy_hom_inv_id,
  homotopy_inv_hom_id := by simpa using
    ((f.homotopy_inv_hom_id.comp_right_id g.hom).comp_left g.inv).trans g.homotopy_inv_hom_id, }

end homotopy_equiv

variables [has_equalizers V] [has_cokernels V] [has_images V] [has_image_maps V] [has_zero_object V]

/--
Homotopic maps induce the same map on homology.
-/
theorem homology_map_eq_of_homotopy (h : homotopy f g) (i : ι) :
  (homology_functor V c i).map f = (homology_functor V c i).map g :=
begin
  dsimp [homology_functor],
  apply eq_of_sub_eq_zero,
  ext,
  simp only [homology.π_map, comp_zero, preadditive.comp_sub],
  dsimp [kernel_subobject_map],
  simp_rw [h.comm i],
  simp only [zero_add, zero_comp, d_next_eq_d_from_from_next, kernel_subobject_arrow_comp_assoc,
    preadditive.comp_add],
  rw [←preadditive.sub_comp],
  simp only [category_theory.subobject.factor_thru_add_sub_factor_thru_right],
  erw [subobject.factor_thru_of_le (D.boundaries_le_cycles i)],
  { simp, },
  { rw [prev_d_eq_to_prev_d_to, ←category.assoc],
    apply image_subobject_factors_comp_self, },
end

/-- Homotopy equivalent complexes have isomorphic homologies. -/
def homology_obj_iso_of_homotopy_equiv (f : homotopy_equiv C D) (i : ι) :
  (homology_functor V c i).obj C ≅ (homology_functor V c i).obj D :=
{ hom := (homology_functor V c i).map f.hom,
  inv := (homology_functor V c i).map f.inv,
  hom_inv_id' := begin
    rw [←functor.map_comp, homology_map_eq_of_homotopy f.homotopy_hom_inv_id,
      category_theory.functor.map_id],
  end,
  inv_hom_id' := begin
    rw [←functor.map_comp, homology_map_eq_of_homotopy f.homotopy_inv_hom_id,
      category_theory.functor.map_id],
  end, }

end

namespace category_theory

variables {W : Type*} [category W] [preadditive W]

/-- An additive functor takes homotopies to homotopies. -/
@[simps]
def functor.map_homotopy (F : V ⥤ W) [F.additive] {f g : C ⟶ D} (h : homotopy f g) :
  homotopy ((F.map_homological_complex c).map f) ((F.map_homological_complex c).map g) :=
{ hom := λ i j, F.map (h.hom i j),
  zero' := λ i j w, by { rw [h.zero i j w, F.map_zero], },
  comm := λ i, begin
    have := h.comm i,
    dsimp [d_next, prev_d] at *,
    rcases c.next i with _|⟨inext,wn⟩;
    rcases c.prev i with _|⟨iprev,wp⟩;
    dsimp [d_next, prev_d] at *;
    { intro h,
      simp [h] },
  end, }

/-- An additive functor preserves homotopy equivalences. -/
@[simps]
def functor.map_homotopy_equiv (F : V ⥤ W) [F.additive] (h : homotopy_equiv C D) :
  homotopy_equiv ((F.map_homological_complex c).obj C) ((F.map_homological_complex c).obj D) :=
{ hom := (F.map_homological_complex c).map h.hom,
  inv := (F.map_homological_complex c).map h.inv,
  homotopy_hom_inv_id := begin
    rw [←(F.map_homological_complex c).map_comp, ←(F.map_homological_complex c).map_id],
    exact F.map_homotopy h.homotopy_hom_inv_id,
  end,
  homotopy_inv_hom_id := begin
    rw [←(F.map_homological_complex c).map_comp, ←(F.map_homological_complex c).map_id],
    exact F.map_homotopy h.homotopy_inv_hom_id,
  end }

end category_theory