import analysis.inner_product_space.pi_L2
import topology.paracompact

--OUTPUT 1
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
begin
  -- Let $\mathcal{A}$ be an open covering of $\mathbb{R}^n$
  assume A : set (euclidean_space ℝ (fin n)),
  assume hA : is_open_cover A,

  -- We now construct a locally finite open refinement $\mathcal{C}$ of $\mathcal{A}$ that covers $\mathbb{R}^n$.
  let C := {s : euclidean_space ℝ (fin n) | ∃ t ∈ A, ∃ m : ℕ, ∃ r : ℝ, s = ball t m r ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ C, ∃ m : ℕ, ball s m 1 ⊂ ball x m 1}},

  -- First, we define a collection of pen balls
  have h1 : {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} = ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m 1} ∩ (euclidean_space ℝ (fin n)) \ {x | ∃ s ∈ ⋃ r : ℕ, {s : euclidean_space ℝ (fin n) | ∃ m : ℕ,ball s m 1 ⊂ ball 0 m
end --Needs more than 2000 tokens!

--OUTPUT 2
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) := sorry

theorem  ℝ_paracompact : paracompact_space euclidean : paracompact_space (euclidean_space ℝ (fin 1)) := sorry

/-`theorem`
Twelve Fold Symmetry of Rhombic Dodecahedron
Let $\struct {D, \mathcal{E}}$ be a rhombic dodecahedron. Then $D$ has a twelve-fold symmetry.
`proof`
Let the twelve edges of $D$ be denoted by $\mathcal{E} = \anset{E_1, E_2, \ldots, E_{12}}$. Since every vertex has valence 3, every edge is incident to two vertices. Let $\mathcal{V}$ denote the set of all vertices of $D$. Then it follows that $\mathcal{E} \subseteq \mathcal{V} \times \mathcal{V}$. We now define a binary relation $R$ on $\mathcal{V}$ such that for all $v, w \in \mathcal{V}$:

- $v R w$ if and only if $(v, w) \in \mathcal{E}$

Let $p$ be a permutation of $\anset{E_1, E_2, \ldots, E_{12}}$. We now define a map $f_p$ on $\mathcal{V}$ such that for any $v \in \mathcal{V}$:

- $f_p(v) = w$ such that $(v, w) \in \mathcal{E}$ and $(v, w) \in p(\mathcal{E})$

We now prove $\mathcal{V}$ has a partition $\mathcal{V} = \bigcup_{v \in \mathcal{V}} C_v$ such that for every $v \in \mathcal{V}$:

- $v \in C_v$,
- $C_v \cap C_w = \phi$ if $v \not= w$, and
- $f_p(x) = y$ for some $x \in C_v$ and for some $y \in C_w$ if and only if $x = y$ and $v = w$.

To prove that $D$ has a twelve-fold symmetry, we will then prove that for every $x \in \mathcal{V}$, there exists a $y \in \mathcal{V}$ such that $x R y$. ${pf}$
{{qed}}
-/
theorem rhombic_dodecahedron_12_fold_symmetry : ∃ f : perm (fin 12) → perm (fin 12), ∀ x : fin 12, f x = x := sorry

end

--OUTPUT 3
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
begin
  -- Let $\mathcal{A}$ be an open covering of $\mathbb{R}^n$.
  assume A : set (euclidean_space ℝ (fin n)),
  assume h1 : is_open A,
  assume h2 : ∀ x : euclidean_space ℝ (fin n), ∃ U ∈ A, x ∈ U,

  -- We now construct a locally finite open refinement $\mathcal{C}$ of $\mathcal{A}$ that covers $\mathbb{R}^n$.
  let C1 : set (euclidean_space ℝ (fin n)) := {B : set (euclidean_space ℝ (fin n)) | ∃ (b : euclidean_space ℝ (fin n)) (m : ℝ),
    is_open B ∧ is_ball ℝ ℝ ((b : euclidean_space ℝ (fin n)).to_fun) m ∧ ⋂ (x : euclidean_space ℝ (fin n)) (h3 : x ∈ B),
    ∃ (U : euclidean_space ℝ (fin n)) (h4 : U ∈ A) (h5 : x ∈ U), B ⊆ U},
  have h3 : ∀ B : set (euclidean_space ℝ (fin n)), ∃ B : set (euclidean_space ℝ (fin n)) (m : ℝ),
    is_open B ∧ is_ball ℝ ℝ (0 : ℝ^(fin n)) m ∧ ⋂ (x : euclidean_space ℝ (fin n)) (h3 : x ∈ B),
    ∃ (U : euclidean_space ℝ (fin n)) (h4 : U ∈ A) (h5 : x ∈ U), B ⊆ U, from by {
      assume B : set (euclidean_space ℝ (fin n)),
      have h4 : ∃ U ∈ A, B ⊆ U, from by {
        have h5 : ∅ ∈ A, from by {
          by_contradiction h6,
          have h7 : ∃ x : euclidean_space ℝ (fin n), x ∉ A, from by {
            let f : ℕ → (euclidean_space ℝ (fin n)), from by {
              assume z : ℕ,
              use (z : ℝ) 0,
            },
            have h8 : ∀ (z : ℕ) (h9 : z ∈ f ⁻¹' A), false, from by {
              assume (z : ℕ) (h9 : z ∈ f ⁻¹' A),
              have h10 : f z ∈ A, from by  {
                simp at h9,
                exact h9,
              },
              have h11 : (0 : ℝ) 0 ∈ A, from by {
                simp at h10,
                exact h10,
              },
              have h12 : ∃ U ∈ A, (0 : ℝ) 0 ∈ U, from by {
                have h13 : ∃ U ∈ A, (0 : ℝ) 0 ∈ U, from by {
                  assume h14,
                  have h15 : (0 : ℝ) 0 ∉ A, from by {
                    assume h16,
                    have h17 : ∅ ∈ A, from by {
                      apply set.subset.subset_singleton,
                      assume x : euclidean_space ℝ (fin n),
                      assume h18 : x ∈ ∅,
                      exact h16,
                    },
                    show false, from h14 h17,
                  },
                  show false, from h15 h16,
                },
                show ∃ U ∈ A, (0 : ℝ) 0 ∈ U, from h13,
              },
              rw show (0 : ℝ) 0 = f z, from rfl,
              exact h12,
            },
            have h14 : f '' A = ∅, from by {
              apply set.subset.antisymm,
              {
                assume x : ℕ,
                assume h15 : x ∈ f '' A,
                show false, from h8 x h15,
              },
              {
                assume x : ℕ,
                assume h15 : x ∈ f '' A,
                show false, from h8 x h15,
              },
            },
            have h16 : A ≠ ∅, from by {
              assume h17,
              have h18 : ∅ = f ''  A, from by {
                rw show ∅ = f '' A, from eq.symm h17,
              },
              show false, from h14 h18,
            },
            show ∃ x : euclidean_space ℝ (fin n), x ∉ A, from ⟨f 0,h16⟩,
          },
          have h8 : ∅ ∉ A, from by {
            assume h9,
            show false, from h5 h9,
          },
          exact h8,
        },
        have h6 : ∃ (U : euclidean_space ℝ (fin n)) (h7 : U ∈ A), ∅ ⊆ U, from ⟨h5,univ_subset_iff.mpr (λ x, true.intro)⟩,
        exact h6,
      },
      let d : ℝ, from by {
        have h7 : ∃ d : ℝ, ∀ (x : ℝ) (h8 : x ∈ B), d < dist x 0, from by {
          have h9 : ∃ (x : ℝ) (h10 : x ∈ B), d < dist x 0, from by {
            have h11 : ∃ (x : ℝ) (h12 : x ∈ B), d < dist x 0, from by {
              have h13 : ∃ (x : ℝ) (h14 : x ∈ B), dist x 0 < d + 1, from by {
                let f : (euclidean_space ℝ (fin n)) → ℝ, from by {
                  assume x : euclidean_space ℝ (fin n),
                  use x.to_fun.sum,
                },
                have h16 : ∀ (g : (euclidean_space ℝ (fin n)) → ℝ), ∃ (x : ℝ) (h15 : x ∈ B), dist x 0 < d + 1, from by {
                  assume (g : (euclidean_space ℝ (fin n)) → ℝ),
                  have h17 : ∃ (x : ℝ) (h15 : x ∈ B), dist x 0 < d + 1, from by {
                    have h18 : ∃ (x : ℝ) (h15 : x ∈ B), dist x 0 < d + 1, from by {
                      have h19 : ∃ (x : ℝ) (h15 : x ∈ B), dist x 0 < d + 1, from by {
                        have h21 : ∃ (x : ℝ) (h15 : x ∈ B), dist x 0 < d + 1, from by {
                          have h23 : ∃ (x : ℝ) (h15 : x ∈ B), dist x 0 < d + 1, from by {
                            have h24 : ∃ (x : ℝ) (h15 : x ∈ B), dist x 0 < d + 1, from by {
                              use f 0,
                              have h25 : ∃ (U : euclidean_space ℝ (fin n)) (h26 : U ∈ A) (h27 : f 0 ∈ U), B ⊆ U, from by {
                                use h4.left,
                                use h4.right.left,
                                use h4.right.right.left,
                                use h4.right.right.right,
                              },
                              have h28 : f 0 ∈ B, from by {
                                simp at h25,
                                exact h25,
                              },
end --Needs more than 2000 tokens!

--OUTPUT 4
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
begin
  let A := λ x : fin n, Ioo x x,
  let K := λ m : ℕ, { x : fin n | 0 ≤ (fintype.card x).val ∧ (fintype.card x).val < m+1},

  have memA_K : ∀ m : ℕ, A (K m) ∈ 𝓝 (0 : fin n), from
    by {
      assume (m : ℕ),
      use (λ x : fin n, x ∈ K m),
      exact ⟨by obviously, by obviously⟩,
    },

  have emptyset_in_memA : ∅ ∈ A ∅, from begin
    use (λ x : fin n, x = 0),
    have h1 : (λ x : fin n, x = 0) 0 = tt, from by obviously,
    have h2 : (λ x : fin n, x = 0) 0 ∈ 𝓝 0, from by {
      use (λ x : fin n, x = 0),
      show ∀ x, x = 0 → x ∈ 𝓝 0, from id,
      exact ⟨by obviously, by obviously⟩,
    },
    exact ⟨h1, h2⟩,
  end,

  have set_emptyset_in_memA_in_Km : ∀ m : ℕ, (A ∅) ∩ K m = ∅, from by {
    assume (m : ℕ),
    show (A ∅) ∩ K m = ∅, from begin
      rw set.inter_eq_self_of_subset_left,
      rw set.inter_eq_self_of_subset_left,
      intro x,
      exact ⟨assume h1, fintype.card_pos_iff.mpr h1.right.left, assume h2, h2.elim $ by {unfold_coes ∅, apply empty_ne_univ}⟩,
    end
  },

  have memA_Km_in_Km : ∀ m : ℕ, A (K m) ∈ A (K m), from by {
    assume m,
    show A (K m) ∈ A (K m), from begin
      use (λ x : fin n, x ∈ K m),
      have h1 : (λ x : fin n, x ∈ K m) ∈ 𝓝 (K m), from by {
        use (λ x : fin n, x ∈ K m),
        show ∀ x, x ∈ K m → x ∈ 𝓝 (K m), from by assume x h1, from memA_K m,
        exact ⟨by obviously, by obviously⟩
      },
      exact ⟨by obviously, h1⟩
    end
  },

  show paracompact_space (euclidean_space ℝ (fin n)), from paracompact_space.intro ⟨emptyset_in_memA, set_emptyset_in_memA_in_Km, memA_Km_in_Km⟩,
end

--OUTPUT 5
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
begin

/-
  -- Let $\mathcal{A}$ be an open covering of $\mathbb{R}^n$.
  assume 𝒜 : opens (euclidean_space ℝ (fin n)),
  -- We now construct a locally finite open refinement $\mathcal{C}$ of $\mathcal{A}$ that covers $\mathbb{R}^n$.
  have h1 : ∃ 𝒞 : opens (euclidean_space ℝ (fin n)), euclidean_space ℝ (fin n).is_open_cover 𝒞 ∧
  -- refinement
  euclidean_space ℝ (fin n).is_open_refinement 𝒜 𝒞 ∧
  -- locally finite
  locally_finite_family 𝒞 ∧
  -- covers $\mathbb{R}^n$
  euclidean_space ℝ (fin n).is_open_cover 𝒞,
  from
  begin
    have h2 : ∃! (B₀ : opens (euclidean_space ℝ (fin n))), B₀ = ∅, from
      by {use (∅ : opens (euclidean_space ℝ (fin n))),
        obviously, obviously,},
    let B₀ := classical.some h2,
    have h3 : B₀ = ∅, from classical.some_spec (exists_unique.exists h2),

    -- Given $m$, set $\Bar{B_m}$ is compact in $\mathbb{R}^n$ by the Heine-Borel theorem, so choose finitely many elements of $\mathcal{A}$ that cover $\Bar{B_m}$ and intersect each one with the open set $\mathbb{R}^n \setminus \Bar{B_{m - 1}}$, and let $\mathcal{C}_{m}$ denote this collection of open sets (each an open subset of an element of $\mathcal{A}$).
    have h4 : ∀ (m : ℕ), ∃ ∃ (Cm : opens (euclidean_space ℝ (fin n))),
    have h5 : ∃ ∃ (Cm : opens (euclidean_space ℝ (fin n))),
      euclidean_space ℝ (fin n).is_open_refinement ∅ Cm ∧
      euclidean_space ℝ (fin n).is_open_refinement 𝒜 Cm ∧
      ∃ (m : ℕ),
      comp 𝒜 Cm ∧
      is_compact (euclidean_space ℝ (fin n) ∪ 𝒜 ∪ ∅ ∪ Cm),
      from
      begin
        use B₀,
        obviously, obviously,
        have h6 : 𝒜 = (∅ : opens (euclidean_space ℝ (fin n))), from 𝒜,
        split,
        obviously, obviously,
        obviously, obviously,
        obviously, obviously,
        obviously, obviously,
        obviously, obviously,
        obviously, obviously,
      end,
  end,
-/
end

--OUTPUT 6
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
begin
  -- Let $\mathcal{A}$ be an open covering of $\mathbb{R}^n$
  assume (h : open_cover (euclidean_space ℝ (fin n))) (U : set (euclidean_space ℝ (fin n))),
  have h1 : ∃ (A : set (fin n → ℝ)), open_cover A, from h,
  have h2 : ∃ (A : set (fin n → ℝ)), is_open A ∧ (∀ (x : fin n → ℝ), x ∈ U → ∃ (a ∈ A), x ∈ a), from h1,
  have h3 : ∃ (A : set (fin n → ℝ)), (∀ (x : fin n → ℝ), x ∈ U → ∃ (a ∈ A), x ∈ a), from h2.left,

  -- We now construct a locally finite open refinement $\mathcal{C}$ of $\mathcal{A}$ that covers $\mathbb{R}^n$
  -- First, we define a collection of pen balls.
  -- Let $B_0 = \phi$, and for each $n \in \mathbb{N}$, let $B_m$ denote the ball of radius $m$
  -- centered at 0.
  have h4 : ∀ (m : ℕ), ∃ (Bm : set (fin n → ℝ)), (∀ (x : fin n → ℝ), (∃ (a ∈ Bm), x ∈ a) ↔ ∀ (y : fin n → ℝ), dist x y < m), from
    by {
      assume (m : ℕ),
      use {x | ∀ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m},
      assume (x : fin n → ℝ) (h : (∃ (a ∈ {x | ∀ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m}), x ∈ a) ↔
        ∀ (y : fin n → ℝ), dist x y < m),
      split,
      assume h1 : ∃ (a ∈ {x | ∀ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m}), x ∈ a,
      assume (y : fin n → ℝ),
      --have h2 : ∀ (y : fin n → ℝ), dist x y < m ↔ ∑ i, (x i - y i) ^ 2 = m, from cauchy_swartz_squared,
      have h2 : dist x y < m ↔ ∑ i, (x i - y i) ^ 2 = m, from cauchy_swartz_squared,
      have h3 : dist x y < m ↔ ∑ i, (x i - y i) ^ 2 < m, from by ring,
      have h4 : dist x y < m ↔ ∑ i, (x i - y i) ^ 2 < m ↔ x ∈ {x | ∀ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m},
        from h,
      rw [h4,h1.right],
      assume h1 : ∀ (y : fin n → ℝ), dist x y < m,
      rw h,
      rw h1,
      split,
      exact set.mem_set_of_eq (eq.symm (dist_self x)),
      assume (y : fin n → ℝ),
      rw eq.symm (dist_self x),
      rw h1,
      exact eq.symm (dist_self x),
    },

  -- Given $m$, set $\Bar{B_m}$ is compact in $\mathbb{R}^n$ by the Heine-Borel theorem, so choose finitely many elements of $\mathcal{A}$ that cover $\Bar{B_m}$
  -- and intersect each one with the open set $\mathbb{R}^n \setminus \Bar{B_{m - 1}}$, and let $\mathcal{C}_{m}$ denote this collection of open sets (each an open subset of an element of $\mathcal{A}$).
  have h5 : ∀ (m : ℕ), ∃ (Cm : set (fin n → ℝ)), (∀ (x : fin n → ℝ), (∃ (a ∈ Cm), x ∈ a) ↔ ∀ (y : fin n → ℝ), dist x y < m) ∧ (∀ (x : fin n → ℝ), ∃ (z ∈ Cm), x ∈ z ↔ x ∈ {x | ∃ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m}),
    from by {
      assume (m : ℕ),
      have h6 : ∃ (Bm : set (fin n → ℝ)), (∀ (x : fin n → ℝ), (∃ (a ∈ Bm), x ∈ a) ↔ ∀ (y : fin n → ℝ), dist x y < m), from h4 m,
      have h7 : ∃ (Bm : set (fin n → ℝ)), (∀ (x : fin n → ℝ), ∃ (z ∈ Bm), x ∈ z ↔ x ∈ {x | ∀ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m}), from by {exact subset_finite_intersection_union (h6.left) (h3.left.left)},
      -- use A ∩ B
      use {x | ∃ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m} ∩ {x | ∀ (y : fin n → ℝ), dist x y < m},
      -- split
      split,
      assume (x : fin n → ℝ) (h : (∃ (a ∈ {x | ∃ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m} ∩ {x | ∀ (y : fin n → ℝ), dist x y < m}), x ∈ a) ↔ ∀ (y : fin n → ℝ), dist x y < m),
        by {
          split,
          assume h1 : ∃ (a ∈ {x | ∃ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m} ∩ {x | ∀ (y : fin n → ℝ), dist x y < m}), x ∈ a,
          rw h,
          rw h6,
          rw h1.right,
          assume h1 : ∀ (y : fin n → ℝ), dist x y < m,
          rw h,
          rw h6,
          rw h1,
          exact set.mem_set_of_eq (eq.symm (dist_self x)),
        },
      assume (x : fin n → ℝ) (h : ∃ (z ∈ {x | ∃ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m} ∩ {x | ∀ (y : fin n → ℝ), dist x y < m}), x ∈ z ↔ x ∈ {x | ∃ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m}),
        by {
          split,
          assume h1 : ∃ (z ∈ {x | ∃ (y : fin n → ℝ), ∑ i, (x i - y i) ^ 2 =  m} ∩ {x | ∀ (y : fin n → ℝ), dist x y < m}), x ∈ z,
          rw h3,
          rw h,
          rw h1.right,
          rw set.inter_def,
          assume h1 : x ∈ {x | ∃ (y : fin n → ℝ), ∑ i
end --Needs more than 2000 tokens!

--OUTPUT 7
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
begin
  assume A : set (euclidean_space ℝ (fin n)),
  assume ha : (is_open_set_cover A),

  have h1 : ∀ m : ℕ, ∀ x : ℝ, ∃! n : ℕ, ∀ j : ℕ, n ≤ j → ∥x - 0∥ < j :=
    assume m : ℕ, assume x : ℝ, exists_unique.intro m
    (exists_unique.intro (le_of_lt (lt_add_one (abs x)))
    (exists_unique.intro rfl rfl)),

  let B_0 := {0 : ℝ^(fin n)},
  let B_m : ℕ → set (euclidean_space ℝ (fin n)) := assume m : ℕ,
    {x : ℝ^(fin n) | ∃ y : ℝ, ∃ m' : ℕ, ∥y∥ = ∥x∥ ∧ y ∈ B(0,m) ∧ m' = m ∧ m' ≤ m + 1 ∧ ∥y∥ < m'},
  let Bar_B_m_n : set (euclidean_space ℝ (fin n)) := λ m : ℕ, closure (B_m m),
  let Bar_B_m_n_plus_1 : set (euclidean_space ℝ (fin n)) := λ m : ℕ, closure (B_m (m+1)),
  let C_m_n : set (euclidean_space ℝ (fin n)) := λ m : ℕ, {(X ∩ (univ \ Bar_B_m_n m)) | X ∈ A ∧ X ⊆ (univ \ Bar_B_m_n_plus_1 m)},
  have h2 : ∀ m : ℕ, ∃ C : set (euclidean_space ℝ (fin n)), is_cover C A ∧ is_locally_finite C :=
    assume m : ℕ, let p := closure (B_m (m+1)) in exists.intro (λ m : ℕ, {(X ∩ (univ \ Bar_B_m_n m)) | X ∈ A ∧ X ⊆ (univ \ p)})
    (and.intro (is_cover_of_subcover (λ m : ℕ, {(X ∩ (univ \ Bar_B_m_n m)) | X ∈ A ∧ X ⊆ (univ \ p)}) (λ m : ℕ, (λ X : set (euclidean_space ℝ (fin n)), (X ∩ (univ \ Bar_B_m_n m)) ∈ A ∧ (X ∩ (univ \ Bar_B_m_n m)) ⊆ (univ \ p)) A)) (show is_locally_finite (λ m : ℕ, {(X ∩ (univ \ Bar_B_m_n m)) | X ∈ A ∧ X ⊆ (univ \ p)}), from
    (is_locally_finite_inter_compact_open (λ m : ℕ, {(X ∩ (univ \ Bar_B_m_n m)) | X ∈ A ∧ X ⊆ (univ \ p)}) (is_locally_finite_of_subcover (λ m : ℕ, {(X ∩ (univ \ Bar_B_m_n m)) | X ∈ A ∧ X ⊆ (univ \ p)}) (λ m : ℕ, {X ∈ A | X ⊆ (univ \ p)}) (is_cover_of_subcover (λ m : ℕ, {X ∈ A | X ⊆ (univ \ p)}) (λ m : ℕ, {X ∈ A | X ⊆ (univ \ p)} A)) (λ m : ℕ, is_open_set.inter_open_set (is_open_set.univ) (is_open_set_set.diff (is_open_set.univ) (B_m (m+1))))) (show is_compact_set (λ m : ℕ, closure (B_m (m+1))), from
      is_compact_set_union (is_compact_set (closure (B_m 0))) (show is_compact_set (λ m : ℕ, closure (B_m (m + 1))), from
        @is_compact_iff_closed_of_heine_borel_is_closed_of_uniform_continuity_of_order_is_compact_of_iota_nat_is_order (λ m : ℕ, closure (B_m (m + 1))) (is_compact_set (closure (B_m 0)))) (is_closed_set_closure (B_m 0)) (show ∀ m : ℕ, is_closed_set (closure (B_m (m + 1))), from
        assume m : ℕ, is_closed_set_closure (B_m (m+1))) (show ∀ m : ℕ, continuous (λ x : ℝ^(fin n), (λ m : ℕ, closure (B_m m)) (m + 1)) x, from
        assume m : ℕ, continuous_at_continuous_on_of_continuous_on_subset (by {apply continuous_closure_of_ball_at_point,} ) (show continuous_on (B_m (m + 1)) (λ x : ℝ^(fin n), (λ m : ℕ, closure (B_m m)) (m + 1)), from
        continuous_on_const)) (show uniform_continuity_on (λ x : ℝ^(fin n), (λ m : ℕ, closure (B_m m)) (m + 1)) (closure (B_m 0)), from
       uniform_continuity_on_of_uniform_continuity_of_subset_of_subset (show uniform_continuity (λ x : ℝ^(fin n), (λ m : ℕ, closure (B_m m)) (m + 1)), from
        (uniform_continuous_on.const (λ m : ℕ, closure (B_m m))).comp (uniform_continuous_on.fst (by {apply uniform_continuous_on_nat,}) (show uniform_continuous_on (λ n : ℕ, closure (B_m (n + 1))) (λ n : ℕ, n + 1), from
        continuous_on.comp (uniform_continuous_on.id (univ)) (show continuous_on (univ) (λ x : ℕ, x + 1), from
        continuous_on_add_one)))) (show (λ x : ℝ^(fin n), (λ m : ℕ, closure (B_m m)) (m + 1)) '' closure (B_m 0) ⊆ closure (B_m 0), from by {apply image_subset_closure,} ) (show ∀ x : ℝ^(fin n), (λ m : ℕ, closure (B_m m)) (m + 1) x ∈ closure (B_m 0), from
        assume x : ℝ^(fin n), by {apply closure_mono, rw iota_succ, apply set.mem_union_left,} )) (is_order_nat (λ m : ℕ, closure (B_m m))))) (is_order_nat (λ m : ℕ, B_m (m + 1))) (show ∀ n x : ℕ, B_m (n + 1) x ⟶ B_m (n + 1) x, from
        assume n x : ℕ, trivial) (show ∀ n x : ℕ, classically (∀ m : ℕ, (λ m : ℕ, closure (B_m m)) m x ⟶ (λ m : ℕ, closure (B_m m)) (m + 1) x), from
        assume n x :
end --Needs more than 2000 tokens!

--OUTPUT 8
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
begin
  -- Let $\mathcal{A}$ be an open covering of $\mathbb{R}^n$.
  assume (U : set (euclidean_space ℝ (fin n))),
  assume (hU : is_open_cover U),

  -- We now construct a locally finite open refinement $\mathcal{C}$ of $\mathcal{A}$ that covers $\mathbb{R}^n$.
  show ∃ (W : set (euclidean_space ℝ (fin n))), is_open_cover W ∧ is_locally_finite W, from
  begin
    let W := set.univ,
    have hW : is_open_cover W, by obviously,
    have hW' : is_locally_finite W, by {sorry},
    have hW'' : W ⊆ U, by obviously,
    have hW_is_refinement : is_refinement W U, from is_refinement_intro hW'' hW,

    have hW'_is_refinement : is_refinement W U ∧ is_locally_finite W, from ⟨hW_is_refinement,hW'⟩,
    show is_locally_finite W ∧ ∃ V : set (euclidean_space ℝ (fin n)), is_open_cover V ∧ is_refinement W V, from hW'_is_refinement,
  end,
end

--OUTPUT 9
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
begin
  /-
    Part 1:
    Goal: For any $x \in \mathbb{R}^n$, construct a ball around it.
  -/
  -- Let $x$ be a variable we chose from $\mathbb{R}^n$
  variable x : euclidean_space ℝ (fin n),
  -- Let $B_0$ be empty set
  def B_0 := ∅,
  -- Construct a ball centered at 0 with radius $m$
  def B_m : set (euclidean_space ℝ (fin n)) := {y : euclidean_space ℝ (fin n) | ∥y - 0∥ ≤ (↑m : ℝ) },
  -- Let $B_m$ be compact in $\mathbb{R}^n$, by Heine-Borel Theorem
  def Bar_B_m : compact_space (euclidean_space ℝ (fin n)) := by apply_instance,
  -- Let $\{B_m\}$ be the collection of all balls centered at 0 with radius $m \in \mathbb{N}$
  def f : ℕ → set (euclidean_space ℝ (fin n)) := λ (m : ℕ), B_m,

  /-
    Part 2:
    Goal: construct a nested collection of sets.
    Let $B_0 = \phi$ be empty set
  -/
  have h1 : B_0 ∪ (B_1 \ B_0) = B_1, from by obviously,
  have h2 : B_1 ∪ (B_2 \ B_1) = B_2, from by obviously,
  have h3 : B_2 ∪ (B_3 \ B_2) = B_3, from by obviously,
  have h4 : B_3 ∪ (B_4 \ B_3) = B_4, from by obviously,
  have h5 : B_4 ∪ (B_5 \ B_4) = B_5, from by obviously,
  -- ... and for any other $B_m, m \in \mathbb{N}$, we have this sequence
  have h6 : B_m ∪ (B_{m+1} \ B_m) = B_{m+1}, from by obviously,
  -- Let $C_0$ be empty set
  def C_0 := ∅,
  /-
    Construct all $C_m$ as follows.
    We have $C_m$ to be the ball centered at 0 with radius $m$ and an open set in $\mathbb{R}^n$
  -/
  def C_m : set (euclidean_space ℝ (fin n)) := {y : euclidean_space ℝ (fin n) | (∥y - 0∥ ≤ (m : ℝ)) ∧ (y ∈ set.univ)},
  -- Let $\{C_m\}$ be the collection of all $C_m$ for any $m \in \mathbb{N}$
  def g : ℕ → set (euclidean_space ℝ (fin n)) := λ (m : ℕ), C_m,
  -- Hence we have a nested sequence of open sets.
  have h7 : B_0 ⊂ B_1, from by rw [set.subset_empty_iff],
  have h8 : B_1 ⊂ B_2, from by rw [set.subset_empty_iff],
  have h9 : B_2 ⊂ B_3, from by rw [set.subset_empty_iff],
  have h10 : B_3 ⊂ B_4, from by rw [set.subset_empty_iff],
  have h11 : B_4 ⊂ B_5, from by rw [set.subset_empty_iff],
  -- ... and for any other $B_m, m \in \mathbb{N}$, we have this sequence
  have h12 : B_m ⊂ B_{m+1}, from by rw [set.subset_empty_iff],

  /-
    Part 3:
    Goal: Give the open cover $\mathcal{A}$, and construct a locally finite open refinement $\mathcal{C}$ of $\mathcal{A}$ that covers $\mathbb{R}^n$.
    Let $\mathcal{A}$ be an open covering of $\mathbb{R}^n$
  -/
  variable A : set (set (euclidean_space ℝ (fin n))),
  -- $\mathcal{A}$ is an open covering of $\mathbb{R}^n$
  assume hA : is_open_cover (euclidean_space ℝ (fin n)) A,
  -- By Heine-Borel theorem, $\Bar{B_m}$ is compact in $\mathbb{R}^n$
  -- Hence we pick finitely many elements of $\mathcal{A}$ that cover $\Bar{B_m}$
  have h13 : ∃ (F : set (set (euclidean_space ℝ (fin n)))), (F ⊆ A) ∧ is_open_cover (euclidean_space ℝ (fin n)) F ∧ compact_space.compact (euclidean_space ℝ (fin n)) F,
     from compact_space.exists_finite_open_cover Bar_B_m (by {apply_instance}),
  -- and intersect each one with the open set $\mathbb{R}^n \setminus \Bar{B_{m - 1}}$
  -- Let $C_m$ denote this collection of open sets (each an open subset of an element of $\mathcal{A}$)
  have h14 : ∃ (C_m : set (set (euclidean_space ℝ (fin n)))), (C_m ⊆ A) ∧ is_open_cover (euclidean_space ℝ (fin n)) C_m ∧ (∀ (x : euclidean_space ℝ (fin n)), x ∈ ⋃₀ C_m → (x ∈ B_m) → (x ∉ Bar_B_m)) ∧ (∀ (x : euclidean_space ℝ (fin n)), x ∈ ⋃₀ C_m → (x ∉ B_m) → (x ∈ Bar_B_m)),
    from exists.elim h13
    (assume (F : set (set (euclidean_space ℝ (fin n)))),
     assume (h15 : (F ⊆ A) ∧ is_open_cover (euclidean_space ℝ (fin n)) F ∧ compact_space.compact (euclidean_space ℝ (fin n)) F),
     assume (h16 : (∀ (x : euclidean_space ℝ (fin n)), x ∈ ⋃₀ F → (∃ (A : set (euclidean_space ℝ (fin n))), (A ∈ F) ∧ (x ∈ A)))),
     use {U : set (euclidean_space ℝ (fin n)) | (U ∈ F) ∧ (U ⊆ B_m)},
     show (∃ (C_m : set (set (euclidean_space ℝ (fin n)))), (C_m ⊆ A) ∧ is_open_cover (euclidean_space ℝ (fin n)) C_m ∧ (∀ (x : euclidean_space ℝ (fin n)), x ∈ ⋃₀ C_m → (x ∈ B_m) → (x ∉ Bar_B_m)) ∧ (∀ (x : euclidean_space ℝ (fin n)), x ∈ ⋃₀ C_m → (x ∉ B_m) → (x ∈ Bar_B_m))), from
     and.intro
     (and.intro (by obviously)
                (and.intro (by {rw [set.forall_mem_iff],
                             intros j jmem,
                             simp [set.mem_
end --Needs more than 2000 tokens!

--OUTPUT 10
theorem ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
begin
  -- Let $\mathcal{A}$ be an open covering of $\mathbb{R}^n$. We now construct a locally finite open refinement $\mathcal{C}$ of $\mathcal{A}$ that covers $\mathbb{R}^n$. First, we define a collection of pen balls. Let $B_0 = \phi$, and for each $n \in \mathbb{N}$, let $B_m$ denote the ball of radius $m$
  -- centered at 0.
  fix (A : set (euclidean_space ℝ (fin n))) (hA : is_open A) (hA_cover : ∀ p ∈ 𝕜, ∃ U ∈ A, p ∈ U),

  let Bm := ball n 0 m,
  let j : ℕ → set (euclidean_space ℝ (fin n)) := λ i, Bm ⊓ A,
  let Πm := (∀ U ∈ j m, ∀ V ∈ j m, V ≠ U → V ∩ U ≠ ∅ → ∃ W ∈ A, W ⊆ U ∩ V),
  let ⟨U, hU⟩ := exists_is_open_ball 0 m,

  have h1 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) < m, from begin
    assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ U),
    by {
      unfold U Bm T,
      unfold ball,
      simp,
      assume hmx,
      exact lt_add_of_pos_of_le (show 0 < m, from nat.pos_of_ne_zero H) hmx,
    }
  end,

  have h2 : ∀ x : euclidean_space ℝ (fin n), x ∈ Bm → x ∈ U, from begin
    assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ Bm),
    show x ∈ U, by {
      unfold U Bm T,
      unfold ball,
      simp,
      assume hmx,
      exact le_add_of_nonneg_of_le hmx (show 0 ≤ m, from nat.zero_le _),
    },
  end,

  have h3 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → x ∈ Bm, from begin
    assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ U),
    have h4 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) < m, from h1 x hx,
    have h5 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) ≤ m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ U),
      show T(x) ≤ m, from le_of_lt (h1 x hx),
    end,
    have h6 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) = m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ U),
      show T(x) = m, from eq_of_le_of_ge (h5 x hx) (h5 x hx),
    end,
    have h7 : ∀ x : euclidean_space ℝ (fin n), x ∈ Bm → T(x) ≤ m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ Bm),
      unfold Bm T,
      unfold ball,
      simp,
      assume hmx,
      exact le_add_of_nonneg_of_le hmx (show 0 ≤ m, from nat.zero_le _),
    end,
    have h8 : ∀ x : euclidean_space ℝ (fin n), x ∈ Bm → T(x) = m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ Bm),
      show T(x) = m, from eq_of_le_of_ge (h7 x hx) (h7 x hx),
    end,
    have h9 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) ≤ m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ U),
      show T(x) ≤ m, from eq.symm (h6 x hx) ▸ (h7 x hx) ,
    end,
    have h10 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) = m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ U),
      show T(x) = m, from eq_of_le_of_ge (h9 x hx) (h9 x hx),
    end,
    show x ∈ Bm, from begin
      unfold Bm T,
      unfold ball,
      simp,
      assume hmx,
      exact eq.symm (h10 x hx) ▸ hmx,
    end,
  end,

  have h4 : ∀ x : euclidean_space ℝ (fin n), x ∈ Bm → x ∈ U, from begin
    assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ Bm),
    have h5 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) < m, from h1 x hx,
    have h6 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) ≤ m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ U),
      show T(x) ≤ m, from le_of_lt (h1 x hx),
    end,
    have h7 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) = m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ U),
      show T(x) = m, from eq_of_le_of_ge (h6 x hx) (h6 x hx),
    end,
    have h8 : ∀ x : euclidean_space ℝ (fin n), x ∈ Bm → T(x) ≤ m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ Bm),
      unfold Bm T,
      unfold ball,
      simp,
      assume hmx,
      exact le_add_of_nonneg_of_le hmx (show 0 ≤ m, from nat.zero_le _),
    end,
    have h9 : ∀ x : euclidean_space ℝ (fin n), x ∈ Bm → T(x) = m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ Bm),
      show T(x) = m, from eq_of_le_of_ge (h8 x hx) (h8 x hx),
    end,
    have h10 : ∀ x : euclidean_space ℝ (fin n), x ∈ U → T(x) ≤ m, from begin
      assume (x : euclidean_space ℝ (fin n)) (hx : x ∈ U),
      show T(x) ≤ m, from eq.symm (h7 x hx) ▸ (h8 x hx),
    end,
end --Needs more than 2000 tokens!

/- FEW SHOT PROMPTS TO CODEX(START)
/--`theorem`
Power Set is Closed under Intersection
Let $S$ be a set.

Let $\powerset S$ be the power set of $S$.

Then:
:$\forall A, B \in \powerset S: A \cap B \in \powerset S$
`proof`
Let $A, B \in \powerset S$.

Then by the definition of power set, $A \subseteq S$ and $B \subseteq S$.

From Intersection is Subset we have that $A \cap B \subseteq A$.

It follows from Subset Relation is Transitive that $A \cap B \subseteq S$.

Thus $A \cap B \in \powerset S$ and closure is proved.
{{qed}}
-/
theorem power_set_intersection_closed {α : Type*} (S : set α) : ∀ A B ∈ 𝒫 S, (A ∩ B) ∈ 𝒫 S :=
begin
  -- $A$ and $B$ are sets. $A$ and $B$ belong to power set of $S$
  assume (A : set α) (hA : A ∈ 𝒫 S) (B : set α) (hB : B ∈ 𝒫 S),
  -- Then $A ⊆ S$ and $B ⊆ S$, by power set definition
  have h1 : (A ⊆ S) ∧ (B ⊆ S), from by {split,apply set.subset_of_mem_powerset,exact hA,apply set.subset_of_mem_powerset,exact hB},
  -- Then $(A ∩ B) ⊆ A$, by intersection of set is a subset
  have h2 : (A ∩ B) ⊆ A, from by apply set.inter_subset_left,
  -- Then $(A ∩ B) ⊆ S$, by subset relation is transitive
  have h3 : (A ∩ B) ⊆ S, from by {apply set.subset.trans h2 h1.left},
  -- Hence $(A ∩ B) ∈  𝒫 S$, by power set definition
  show (A ∩ B) ∈  𝒫 S, from by {apply set.mem_powerset h3},
end

/--`theorem`
Square of Sum
 :$\forall x, y \in \R: \paren {x + y}^2 = x^2 + 2 x y + y^2$
`proof`
Follows from the distribution of multiplication over addition:

{{begin-eqn}}
{{eqn | l = \left({x + y}\right)^2
      | r = \left({x + y}\right) \cdot \left({x + y}\right)
}}
{{eqn | r = x \cdot \left({x + y}\right) + y \cdot \left({x + y}\right)
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x \cdot x + x \cdot y + y \cdot x + y \cdot y
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x^2 + 2xy + y^2
      | c =
}}
{{end-eqn}}
{{qed}}
-/
theorem square_of_sum (x y : ℝ) : (x + y)^2 = (x^2 + 2*x*y + y^2) :=
begin
  -- expand the power
  calc (x + y)^2 = (x+y)*(x+y) : by rw sq
  -- distributive property of multiplication over addition gives:
  ... = x*(x+y) + y*(x+y) : by rw add_mul
  -- applying the above property further gives:
  ... = x*x + x*y + y*x + y*y : by {rw [mul_comm x (x+y),mul_comm y (x+y)], rw [add_mul,add_mul], ring}
  -- rearranging the terms using commutativity and adding gives:
  ... = x^2 + 2*x*y + y^2 : by {repeat {rw ← sq}, rw mul_comm y x, ring}
end

/--`theorem`
Identity of Group is Unique
Let $\struct {G, \circ}$ be a group. Then there is a unique identity element $e \in G$.
`proof`
From Group has Latin Square Property, there exists a unique $x \in G$ such that:
:$a x = b$

and there exists a unique $y \in G$ such that:
:$y a = b$

Setting $b = a$, this becomes:

There exists a unique $x \in G$ such that:
:$a x = a$

and there exists a unique $y \in G$ such that:
:$y a = a$

These $x$ and $y$ are both $e$, by definition of identity element.
{{qed}}
-/
theorem group_identity_unique {G : Type*} [group G] : ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a :=
begin
  -- Group has Latin Square Property
  have h1 : ∀ a b : G, ∃! x : G, a * x = b, from by {
    assume a b : G, use a⁻¹ * b, obviously, },
  have h2 : ∀ a b : G, ∃! y : G, y * a = b, from by {
    assume a b : G, use b * a⁻¹, obviously, },

  -- Setting $b = a$, this becomes:
  have h3 : ∀ a : G, ∃! x : G, a * x = a, from
    assume a : G, h1 a a,
  have h4 : ∀ a : G, ∃! y : G, y * a = a, from
    assume a : G, h2 a a,

  -- These $x$ and $y$ are both $(1 : G)$, by definition of identity element
  have h5 : ∀ a : G, classical.some (h3 a).exists = (1 : G), from assume a :G,
    exists_unique.unique (h3 a) (classical.some_spec (exists_unique.exists (h3 a)))
    (mul_one a),
  have h6 : ∀ a : G, classical.some (h4 a).exists = (1 : G), from assume a : G,
    exists_unique.unique (h4 a) (classical.some_spec (exists_unique.exists (h4 a))) (one_mul a),

  show ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a, from by {
    use (1 : G),
    have h7 : ∀ e : G, (∀ a : G, e * a = a ∧ a * e = a) → e = 1, from by {
      assume (e : G) (hident : ∀ a : G, e * a = a ∧ a * e = a),
      have h8 : ∀ a : G, e = classical.some (h3 a).exists, from assume (a : G),
        exists_unique.unique (h3 a) (hident a).right
        (classical.some_spec (exists_unique.exists (h3 a))),
      have h9 : ∀ a : G, e = classical.some (h4 a).exists, from assume (a : G),
        exists_unique.unique (h4 a) (hident a).left
        (classical.some_spec (exists_unique.exists (h4 a))),
      show e = (1 : G), from eq.trans (h9 e) (h6 _),
    },
    exact ⟨by obviously, h7⟩,
  }
end

/--`theorem`
\mathbb{R}^n is paracompact
$\mathbb{R}^n$ is paracompact for all $n$.
`proof`
Let $\mathcal{A}$ be an open covering of $\mathbb{R}^n$. We now construct a locally finite open refinement $\mathcal{C}$ of $\mathcal{A}$ that covers $\mathbb{R}^n$. First, we define a collection of pen balls. Let $B_0 = \phi$, and for each $n \in \mathbb{N}$, let $B_m$ denote the ball of radius $m$
centered at 0. Given $m$, set $\Bar{B_m}$ is compact in $\mathbb{R}^n$ by the Heine-Borel theorem, so choose finitely many elements of $\mathcal{A}$ that cover $\Bar{B_m}$ and intersect each one with the open set $\mathbb{R}^n \setminus \Bar{B_{m - 1}}$, and let $\mathcal{C}_{m}$ denote this collection of open sets (each an open subset of an element of $\mathcal{A}$). So $\mathcal{C} = \bigcup_{m = 0}^{\infty} \mathcal{C}_m$ is an open refinement of $\mathcal{A}$. Note that $\mathcal{C}$ covers $\mathbb{R}^n$ since for any $x \in \mathbb{R}^n$, there is a smallest $m \in \mathbb{N}$ such that $x \in \Bar{B_{m}}$ (namely, some $m$ where $\rVert x \lVert \leq m \leq \rVert x \lVert + 1$), and so $x$ is an element of $\mathcal{C}_m$. Now collection $\mathcal{C}$ is locally finite since for given $x \in \mathbb{R}^n$, neighborhood $B_m$ intersects only finitely many elements of $\mathcal{C}$, namely those elements in collection $\mathcal{C}_1 \cup \mathcal{C}_2 \cup \cdots \mathcal{C}_m$. So $\mathcal{C}$ is a locally finite open refinement of $\mathcal{A}$ that covers $\mathbb{R}^n$, hence $\mathbb{R}^n$ is paracompact.

QED
-/
theorem  ℝn_paracompact (n : ℕ) : paracompact_space (euclidean_space ℝ (fin n)) :=
FEW SHOT PROMPTS TO CODEX(END)-/


import for_mathlib.short_complex_projections
import for_mathlib.homological_complex_abelian
import for_mathlib.homology_map_datum
import for_mathlib.abelian_sheaves.functor_category
import for_mathlib.short_complex_functor_category

noncomputable theory

open category_theory category_theory.category category_theory.limits
open_locale zero_object

universes v

namespace short_complex

section construction

variables {C : Type*} [category C] [has_zero_morphisms C]
variables {J : Type*} [category J] (F : J ⥤ short_complex C)
  [has_colimit (F ⋙ π₁)] [has_colimit (F ⋙ π₂)] [has_colimit (F ⋙ π₃)]

@[simps]
def colimit_cocone.cocone : cocone F :=
{ X := mk (colim_map (𝟙 F ◫ φ₁₂)) (colim_map (𝟙 F ◫ φ₂₃)) begin
    ext,
    dsimp,
    simp only [ι_colim_map_assoc, nat_trans.hcomp_app, φ₁₂_app, nat_trans.id_app, π₂_map,
      ι_colim_map, φ₂₃_app, π₃_map, assoc, comp_zero],
    erw [composable_morphisms.id_τ₂, id_comp, (F.obj j).zero_assoc, zero_comp],
  end,
  ι :=
    { app := λ j, begin
        refine ⟨colimit.ι (F ⋙ π₁) j, colimit.ι (F ⋙ π₂) j, colimit.ι (F ⋙ π₃) j, _, _⟩,
        { dsimp,
          simp only [ι_colim_map, nat_trans.hcomp_app, φ₁₂_app, nat_trans.id_app, π₂_map,
            assoc],
          erw [composable_morphisms.id_τ₂, id_comp], },
        { dsimp,
          simp only [ι_colim_map, nat_trans.hcomp_app, φ₂₃_app, nat_trans.id_app, π₃_map,
            assoc],
          erw [composable_morphisms.id_τ₃, id_comp], },
      end,
      naturality' := λ i j f, begin
        ext,
        { dsimp, simpa only [comp_id] using colimit.w (F ⋙ π₁) f, },
        { dsimp, simpa only [comp_id] using colimit.w (F ⋙ π₂) f, },
        { dsimp, simpa only [comp_id] using colimit.w (F ⋙ π₃) f, },
      end }, }

def colimit_cocone : colimit_cocone F :=
{ cocone := colimit_cocone.cocone F,
  is_colimit :=
  { desc := λ s, begin
      refine ⟨colimit.desc (F ⋙ π₁) (π₁.map_cocone s),
        colimit.desc (F ⋙ π₂) (π₂.map_cocone s),
        colimit.desc (F ⋙ π₃) (π₃.map_cocone s), _, _⟩,
      { ext,
        dsimp,
        simp only [ι_colim_map_assoc, nat_trans.hcomp_app, φ₁₂_app, nat_trans.id_app,
          π₂_map, colimit.ι_desc, functor.map_cocone_ι_app, assoc, colimit.ι_desc_assoc, π₁_map],
        erw [composable_morphisms.id_τ₂, id_comp],
        exact (s.ι.app j).comm₁₂, },
      { ext,
        dsimp,
        simp only [ι_colim_map_assoc, nat_trans.hcomp_app, φ₂₃_app, nat_trans.id_app,
          π₃_map, colimit.ι_desc, functor.map_cocone_ι_app, assoc, colimit.ι_desc_assoc, π₂_map],
        erw [composable_morphisms.id_τ₃, id_comp],
        exact (s.ι.app j).comm₂₃, },
    end,
    fac' := λ s j, begin
      ext,
      { dsimp, simp only [colimit.ι_desc, functor.map_cocone_ι_app, π₁_map], },
      { dsimp, simp only [colimit.ι_desc, functor.map_cocone_ι_app, π₂_map], },
      { dsimp, simp only [colimit.ι_desc, functor.map_cocone_ι_app, π₃_map], },
    end,
    uniq' := λ s m hm, begin
      have h₁ := λ j, congr_arg (λ (φ : F.obj j ⟶ s.X), π₁.map φ) (hm j),
      have h₂ := λ j, congr_arg (λ (φ : F.obj j ⟶ s.X), π₂.map φ) (hm j),
      have h₃ := λ j, congr_arg (λ (φ : F.obj j ⟶ s.X), π₃.map φ) (hm j),
      dsimp at h₁ h₂ h₃,
      ext,
      { dsimp, simp only [h₁, colimit.ι_desc, functor.map_cocone_ι_app, π₁_map], },
      { dsimp, simp only [h₂, colimit.ι_desc, functor.map_cocone_ι_app, π₂_map], },
      { dsimp, simp only [h₃, colimit.ι_desc, functor.map_cocone_ι_app, π₃_map], },
    end, }, }

instance : has_colimit F := ⟨nonempty.intro (colimit_cocone F)⟩

def π₁_preserves_colimit : preserves_colimit F (π₁ : short_complex C ⥤ C) :=
preserves_colimit_of_preserves_colimit_cocone (colimit_cocone F).is_colimit
  (is_colimit.of_iso_colimit (get_colimit_cocone (F ⋙ π₁)).is_colimit
    (cocones.ext (iso.refl _) (λ j, comp_id _)))

def π₂_preserves_colimit : preserves_colimit F (π₂ : short_complex C ⥤ C) :=
preserves_colimit_of_preserves_colimit_cocone (colimit_cocone F).is_colimit
  (is_colimit.of_iso_colimit (get_colimit_cocone (F ⋙ π₂)).is_colimit
    (cocones.ext (iso.refl _) (λ j, comp_id _)))

def π₃_preserves_colimit : preserves_colimit F (π₃ : short_complex C ⥤ C) :=
preserves_colimit_of_preserves_colimit_cocone (colimit_cocone F).is_colimit
  (is_colimit.of_iso_colimit (get_colimit_cocone (F ⋙ π₃)).is_colimit
    (cocones.ext (iso.refl _) (λ j, comp_id _)))

end construction

section preserves

variables {C : Type*} [category C] [has_zero_morphisms C]
variables {J D : Type*} [category J] [category D]

def π₁₂₃_reflects_colimits {F : J ⥤ short_complex C} (s : cocone F)
  (h₁ : is_colimit (π₁.map_cocone s)) (h₂ : is_colimit (π₂.map_cocone s))
  (h₃ : is_colimit (π₃.map_cocone s)) :
  is_colimit s :=
begin
  haveI : has_colimit (F ⋙ π₁) := ⟨nonempty.intro ⟨_, h₁⟩⟩,
  haveI : has_colimit (F ⋙ π₂) := ⟨nonempty.intro ⟨_, h₂⟩⟩,
  haveI : has_colimit (F ⋙ π₃) := ⟨nonempty.intro ⟨_, h₃⟩⟩,
  refine is_colimit.of_iso_colimit (colimit_cocone F).is_colimit (cocones.ext _ _),
  { suffices : is_iso ((colimit_cocone F).is_colimit.desc s),
    { haveI := this,
      exact as_iso ((colimit_cocone F).is_colimit.desc s), },
    apply is_iso_of_is_isos,
    { exact is_iso.of_iso (is_colimit.cocone_point_unique_up_to_iso (colimit.is_colimit _) h₁), },
    { exact is_iso.of_iso (is_colimit.cocone_point_unique_up_to_iso (colimit.is_colimit _) h₂), },
    { exact is_iso.of_iso (is_colimit.cocone_point_unique_up_to_iso
        (colimit.is_colimit _) h₃), }, },
  { intro j,
    simp only [as_iso_hom, is_colimit.fac], },
end

def π₁₂₃_reflect_preserves_colimits (G : J ⥤ D) (F : D ⥤ short_complex C)
  (h₁ : preserves_colimit G (F ⋙ π₁)) (h₂ : preserves_colimit G (F ⋙ π₂))
  (h₃ : preserves_colimit G (F ⋙ π₃)) : preserves_colimit G F :=
⟨λ s hs, π₁₂₃_reflects_colimits _
  (@is_colimit_of_preserves _ _ _ _ _ _ G (F ⋙ π₁) _ hs _)
  (@is_colimit_of_preserves _ _ _ _ _ _ G (F ⋙ π₂) _ hs _)
  (@is_colimit_of_preserves _ _ _ _ _ _ G (F ⋙ π₃) _ hs _)⟩

variable (J)

def preserves_colimits_of_shape_of_projections (F : D ⥤ short_complex C)
  (h₁ : preserves_colimits_of_shape J (F ⋙ π₁))
  (h₂ : preserves_colimits_of_shape J (F ⋙ π₂))
  (h₃ : preserves_colimits_of_shape J (F ⋙ π₃)) :
  preserves_colimits_of_shape J F :=
⟨by { intro G, apply π₁₂₃_reflect_preserves_colimits; apply_instance, }⟩

end preserves

section functor_homological_complex

variables {C : Type*} [category C] [abelian C]
variables {M : Type*} {c : complex_shape M}
variables {J : Type*} [category J]

instance zero_preserves_colimits_of_shape {D : Type*} [category D]:
  preserves_colimits_of_shape J (0 : D ⥤ C) :=
⟨λ F, ⟨λ s hs,
{ desc := λ t, 0,
  fac' := λ t j, begin
    dsimp,
    apply is_zero.eq_of_src,
    apply is_zero.obj,
    apply is_zero_zero,
  end,
  uniq' := λ t m j, begin
    dsimp,
    apply is_zero.eq_of_src,
    apply is_zero.obj,
    apply is_zero_zero,
  end, }⟩⟩

lemma functor_homological_complex_π₁_iso_eval (i j : M) (hij : c.rel j i) :
  functor_homological_complex C c i ⋙ π₁ ≅ homological_complex.eval C c j :=
nat_iso.of_components (λ X, X.X_prev_iso hij)
(λ X Y f, begin
  dsimp,
  simp only [homological_complex.hom.prev_eq f hij, assoc, iso.inv_hom_id, comp_id],
end)

lemma functor_homological_complex_π₃_iso_eval (i j : M) (hij : c.rel i j) :
  functor_homological_complex C c i ⋙ π₃ ≅ homological_complex.eval C c j :=
nat_iso.of_components (λ X, X.X_next_iso hij)
(λ X Y f, begin
  dsimp,
  simp only [homological_complex.hom.next_eq f hij, assoc, iso.inv_hom_id, comp_id],
end)

instance (i : M) [has_colimits_of_shape J C] :
  preserves_colimits_of_shape J (short_complex.functor_homological_complex C c i) :=
begin
  apply preserves_colimits_of_shape_of_projections;
  { exact (infer_instance : preserves_colimits_of_shape J (homological_complex.eval C c _)), },
end

end functor_homological_complex

section functor_homology

variables {C : Type*} [category.{v} C] [abelian C]
variables {M : Type*} {c : complex_shape M}
  {J : Type v} [small_category J] [is_filtered J]
  [has_colimits_of_shape J C]
  [preserves_finite_limits (limits.colim : (J ⥤ C) ⥤ C)]
  [preserves_finite_colimits (limits.colim : (J ⥤ C) ⥤ C)]

namespace homology_functor_preserves_colimit

variable (F : short_complex (J ⥤ C))

def iso_datum := homology_iso_datum.tautological' F.1.f F.1.g F.2

instance (j : J) : preserves_finite_limits ((evaluation J C).obj j) :=
⟨by { intro F, introI, introI, apply_instance, }⟩
instance (j : J) : preserves_finite_colimits ((evaluation J C).obj j) :=
⟨by { intro F, introI, introI, apply_instance, }⟩
instance (j : J) : functor.additive ((evaluation J C).obj j) := { }
instance colim_additive : functor.additive (colim : (J ⥤ C) ⥤ C) := { }

@[simps]
def nat_trans_ι (j : J) : (evaluation J C).obj j ⟶ (colim : (J ⥤ C) ⥤ C) :=
{ app := λ F, colimit.ι F j,
  naturality' := λ F₁ F₂ φ, by { dsimp, simp only [colimit.ι_map], }, }

def iso_datum₁ := (iso_datum F).apply_exact_functor (colim : (J ⥤ C) ⥤ C)

def F₀ := functor_category_equivalence.functor.obj F

def e₁ : (F₀ F) ⋙ homology_functor ≅ (iso_datum F).H :=
nat_iso.of_components
  (λ j, ((iso_datum F).apply_exact_functor ((evaluation J C).obj j)).iso.symm)
  (λ i j f, begin
    simp only [functor.comp_map, iso.symm_hom],
    erw ((iso_datum F).map_nat_trans ((evaluation J C).map f)).homology_map_eq,
    simpa only [evaluation_map_app, assoc, iso.hom_inv_id, comp_id,
      iso.cancel_iso_inv_left],
  end)

def e₂ : colim.map_short_complex.obj F ≅ (colimit_cocone.cocone (F₀ F)).X :=
begin
  refine iso_mk _ _ _ _ _,
  { refine colim.map_iso (nat_iso.of_components (λ j, iso.refl _) (λ i j f, _)),
    dsimp, erw [id_comp, comp_id], refl, },
  { refine colim.map_iso (nat_iso.of_components (λ j, iso.refl _) (λ i j f, _)),
    dsimp, erw [id_comp, comp_id], refl, },
  { refine colim.map_iso (nat_iso.of_components (λ j, iso.refl _) (λ i j f, _)),
    dsimp, erw [id_comp, comp_id], refl, },
  { ext, dsimp, simp only [colimit.ι_map_assoc, colimit.ι_map, nat_iso.of_components_hom_app,
      iso.refl_hom, id_comp, ι_colim_map, nat_trans.hcomp_app, φ₁₂_app, nat_trans.id_app,
      π₂_map, assoc], erw id_comp, refl, },
  { ext, dsimp, simp only [colimit.ι_map_assoc, colimit.ι_map, nat_iso.of_components_hom_app,
      iso.refl_hom, id_comp, ι_colim_map, nat_trans.hcomp_app, φ₂₃_app, nat_trans.id_app,
      π₃_map, assoc], erw id_comp, refl, },
end

def e₃ : colimit (F₀ F ⋙ homology_functor) ≅ (colim.map_short_complex.obj F).homology :=
colim.map_iso (e₁ F) ≪≫ (iso_datum₁ F).iso

def e₄ : colimit (F₀ F ⋙ homology_functor) ≅ (colimit_cocone.cocone (F₀ F)).X.homology :=
e₃ F ≪≫ homology_functor.map_iso (e₂ F)

lemma compatibility (j : J) : (colimit.cocone (F₀ F ⋙ homology_functor)).ι.app j ≫
  (e₃ F).hom = homology_functor.map ((nat_trans_ι j).map_short_complex.app F) :=
begin
  rw ((iso_datum F).map_nat_trans (nat_trans_ι j)).homology_map_eq,
  dsimp only [e₁, e₃, iso_datum₁, nat_iso.of_components],
  simpa only [colimit.cocone_ι, iso.trans_hom, functor.map_iso_hom, colimit.ι_map_assoc,
    iso.symm_hom, nat_trans_ι_app, iso.cancel_iso_hom_right_assoc, iso.cancel_iso_inv_left],
end

lemma preserves : preserves_colimit (F₀ F) short_complex.homology_functor :=
⟨λ s hs, begin
  have e₁ : s ≅ colimit_cocone.cocone (F₀ F),
  { refine is_initial.unique_up_to_iso _ _,
    all_goals { equiv_rw (cocone.is_colimit_equiv_is_initial _).symm, },
    exacts [hs, (colimit_cocone (F₀ F)).is_colimit], },
  suffices : is_colimit (homology_functor.map_cocone (colimit_cocone.cocone (F₀ F))),
  { exact is_colimit.of_iso_colimit this
      ((cocones.functoriality _ homology_functor).map_iso e₁.symm), },
  clear e₁ hs s,
  refine is_colimit.of_iso_colimit (colimit.is_colimit (F₀ F ⋙ homology_functor))
     (cocones.ext (e₄ F) _),
  intro j,
  dsimp only [functor.map_cocone, cocones.functoriality, e₄, iso.trans, functor.map_iso],
  rw [← assoc, compatibility, ← homology_functor.map_comp],
  congr' 1,
  ext1,
  all_goals
  { dsimp [e₂], simp only [colimit.ι_map, nat_iso.of_components_hom_app,
      iso.refl_hom, id_comp], },
end⟩

end homology_functor_preserves_colimit

instance (F₀ : J ⥤ short_complex C) : preserves_colimit F₀ short_complex.homology_functor :=
begin
  let F := functor_category_equivalence.inverse.obj F₀,
  haveI : preserves_colimit (homology_functor_preserves_colimit.F₀ F) homology_functor
    := homology_functor_preserves_colimit.preserves F,
  have h : homology_functor_preserves_colimit.F₀ F ≅ F₀ :=
    functor_category_equivalence.counit_iso.app F₀,
  exact preserves_colimit_of_iso_diagram short_complex.homology_functor h,
end

instance : preserves_colimits_of_shape J
  (short_complex.homology_functor : short_complex C ⥤ C) := ⟨λ F, infer_instance⟩

end functor_homology

end short_complex


import Lean.Meta

syntax (name := stx_rfl) "stx_rfl" : tactic

open Lean.Elab.Tactic Lean Meta in
@[tactic stx_rfl] def syntacticRefl : Tactic := fun _ => do
  let goal ← getMainGoal
  let goalType ← goal.getType
  match goalType.app3? ``Eq with
  | none => throwTacticEx `stx_rfl goal m!"equality expected"
  | some (_,lhs,rhs) =>

    let lhs ← instantiateMVars lhs
    let rhs ← instantiateMVars rhs

    -- This is a very crude test and maybe too strict
    -- In my use case I wand defEq witout zeta reduction
    if lhs == rhs then
      goal.applyRefl
    else
      throwTacticEx `stx_rfl goal m!"{← Lean.Meta.ppExpr lhs} and {← Lean.Meta.ppExpr rhs} are not syntactically equal!"

example : 0 = 0 :=
by
  stx_rfl


import for_mathlib.derived.les_facts
import liquid
import Lbar.functor
import condensed.projective_resolution
import condensed.condensify
import condensed.bd_lemma
import breen_deligne.eg

import for_mathlib.derived.ext_coproducts
import condensed.ab4
import Lbar.squares
import pseudo_normed_group.QprimeFP
import for_mathlib.acyclic
import free_pfpng.acyclic
import for_mathlib.SemiNormedGroup_ulift
import for_mathlib.bicartesian4
import for_mathlib.has_homology_aux

import for_mathlib.derived.Ext_lemmas

noncomputable theory

universes u

open opposite category_theory category_theory.limits
open_locale nnreal zero_object

variables (r r' : ℝ≥0)
variables [fact (0 < r)] [fact (0 < r')] [fact (r < r')] [fact (r < 1)] [fact (r' < 1)]

abbreviation SemiNormedGroup.to_Cond (V : SemiNormedGroup.{u}) := Condensed.of_top_ab V

section

open bounded_homotopy_category

variables (BD : breen_deligne.data)
variables (κ κ₂ : ℝ≥0 → ℕ → ℝ≥0)
variables [∀ (c : ℝ≥0), BD.suitable (κ c)] [∀ n, fact (monotone (function.swap κ n))]
variables [∀ (c : ℝ≥0), BD.suitable (κ₂ c)] [∀ n, fact (monotone (function.swap κ₂ n))]
variables (M : ProFiltPseuNormGrpWithTinv₁.{u} r')
variables (V : SemiNormedGroup.{u}) [complete_space V] [separated_space V]

lemma ExtQprime_iso_aux_system_aux (c : ℝ≥0) (k i : ℤ) (hi : i > 0) :
  is_zero (((Ext' i).obj (op (((homological_complex.embed complex_shape.embedding.nat_down_int_up).obj
      ((QprimeFP_nat.{u} r' BD κ M).obj c)).X k))).obj V.to_Cond) :=
begin
  rcases k with (_|_)|_,
  { apply free_acyclic.{u} _ V i hi },
  { apply bounded_derived_category.Ext'_zero_left_is_zero, refine (is_zero_zero _).op },
  { apply free_acyclic.{u} _ V i hi },
end

def embed_unop {𝓐 : Type*} [category 𝓐] [abelian 𝓐] :
  (homological_complex.embed complex_shape.embedding.nat_down_int_up).op ⋙
    @homological_complex.unop_functor 𝓐 _ _ _ _ ≅
  homological_complex.unop_functor ⋙
    homological_complex.embed complex_shape.embedding.nat_up_int_down :=
begin
  refine nat_iso.of_components _ _,
  { intro X, refine homological_complex.hom.iso_of_components _ _,
    { rintro ((_|n)|n),
      { exact iso.refl _ },
      { refine is_zero.iso (is_zero_zero _).unop (is_zero_zero _), },
      { exact iso.refl _ }, },
    { rintro i (j|(_|j)) (rfl : _ = _),
      { apply is_zero.eq_of_src, exact (is_zero_zero _).unop },
      { dsimp only [iso.refl_hom], erw [category.id_comp, category.comp_id], refl },
      { dsimp only [iso.refl_hom], erw [category.id_comp, category.comp_id], refl }, } },
  { intros X Y f, ext ((_|n)|n),
    { dsimp only [homological_complex.comp_f, homological_complex.hom.iso_of_components_hom_f, iso.refl_hom],
      erw [category.id_comp, category.comp_id], refl },
    { apply is_zero.eq_of_tgt, exact is_zero_zero _ },
    { dsimp only [homological_complex.comp_f, homological_complex.hom.iso_of_components_hom_f, iso.refl_hom],
      erw [category.id_comp, category.comp_id], refl } }
end
.

-- move me
lemma nat_up_int_down_c_iff : complex_shape.embedding.nat_up_int_down.c_iff :=
λ i j, complex_shape.embedding.nat_down_int_up_c_iff j i

def forget₂_unop :
  ((forget₂ SemiNormedGroup Ab).op.map_homological_complex (complex_shape.down ℕ)).op ⋙
  homological_complex.unop_functor ≅
  homological_complex.unop_functor ⋙
  (forget₂ SemiNormedGroup Ab).map_homological_complex (complex_shape.down ℕ).symm :=
begin
  refine nat_iso.of_components _ _,
  { intro X, refine homological_complex.hom.iso_of_components _ _,
    { intro n, exact iso.refl _ },
    { rintro i j (rfl : _ = _), dsimp only [iso.refl_hom],
      rw [category.id_comp, category.comp_id], refl } },
  { intros X Y f, ext n,
    dsimp only [homological_complex.comp_f, homological_complex.hom.iso_of_components_hom_f, iso.refl_hom],
    rw [category.id_comp, category.comp_id], refl }
end
.

def preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab
  (M : Condensed.{u} Ab.{u+1}) (X : Profinite) :
  (preadditive_yoneda.obj M).obj (op $ CondensedSet_to_Condensed_Ab.obj (Profinite_to_Condensed.obj X)) ≅
  M.val.obj (op X) :=
let e := Condensed_Ab_CondensedSet_adjunction.hom_equiv X.to_Condensed M in
add_equiv.to_AddCommGroup_iso $
{ to_fun := λ t, yoneda'_equiv _ _ (e t).val,
  inv_fun := λ t, e.symm $ ⟨(yoneda'_equiv _ _).symm $ by apply t⟩,
  left_inv := λ t, begin
    dsimp only,
    apply_fun e, rw equiv.apply_symm_apply, ext1,
    dsimp only, erw equiv.apply_symm_apply,
  end,
  right_inv := λ t, begin
    dsimp only,
    rw equiv.apply_symm_apply,
    rw equiv.apply_symm_apply,
  end,
  map_add' := begin
    intros x y,
    refl,
  end }

@[reassoc]
lemma preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab_natural
  {M₁ M₂ : Condensed.{u} Ab.{u+1}} (f : M₁ ⟶ M₂) (X : Profinite) :
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab M₁ X).hom ≫ f.val.app _ =
  (preadditive_yoneda.map f).app _ ≫
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab M₂ X).hom :=
by { ext, refl }

@[reassoc]
lemma preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab_natural'
  (M : Condensed.{u} Ab.{u+1}) {X Y : Profinite.{u}} (f : X ⟶ Y) :
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab M Y).hom ≫ M.val.map f.op =
  (preadditive_yoneda.obj M).map (CondensedSet_to_Condensed_Ab.map $
    Profinite_to_Condensed.map f).op ≫
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab M X).hom :=
begin
  ext t,
  rw comp_apply,
  rw comp_apply,
  dsimp [preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab, adjunction.whisker_right],
  simp only [← nat_trans.comp_app],
  rw ← grothendieck_topology.to_sheafify_naturality_assoc,
  dsimp [functor.right_unitor],
  simp only [← comp_apply, category.assoc, ← nat_trans.comp_app, ← nat_trans.comp_app_assoc],
  simp only [← nat_trans.naturality, functor.comp_map, category.assoc],
  refl,
end

end


class L1 (α : Type u) where
  add    : α → α → α
  addc1  : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x

instance L1.toAdd [inst : L1 α] : Add α := { inst with }

class L2 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x

instance L2.toL1 [inst : L2 α] : L1 α := { inst with }

class L3 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x

instance L3.toL2 [inst : L3 α] : L2 α := { inst with }

class L4 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x

instance L4.toL3 [inst : L4 α] : L3 α := { inst with }

class L5 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x

instance L5.toL4 [inst : L5 α] : L4 α := { inst with }

class L6 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x

instance L6.toL5 [inst : L6 α] : L5 α := { inst with }

class L7 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) y x

instance L7.toL6 [inst : L7 α] : L6 α := { inst with }

class L8 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) y x
  addc8 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) y x

instance L8.toL7 [inst : L8 α] : L7 α := { inst with }

class L9 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) y x
  addc8 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) y x
  addc9 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ (@L8.toL7 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7, addc8⟩)))))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ (@L8.toL7 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7, addc8⟩)))))))) y x

instance L9.toL8 [inst : L9 α] : L8 α := { inst with }

class T1 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) y x
  addc8 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) y x
  addc9 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ (@L8.toL7 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7, addc8⟩)))))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ (@L8.toL7 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7, addc8⟩)))))))) y x

-- slow
instance T1.toL9 [inst : T1 α] : L9 α := { inst with }

class T2 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @Add.add α ⟨add⟩ x y = @Add.add α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) x y = @Add.add α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) y x
  addc8 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) y x
  addc9 : ∀ (x y : α), @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ (@L8.toL7 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7, addc8⟩)))))))) x y = @Add.add α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ (@L8.toL7 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7, addc8⟩)))))))) y x

-- slow
instance T2.toL9 [inst : T2 α] : L9 α := { inst with }

set_option pp.all true in
-- #print T2.toL9

axiom C : Type
axiom C.add   : C → C → C

noncomputable instance C.T1 : T1 C := ⟨add, sorry, sorry, sorry, sorry, sorry, sorry, sorry, sorry, sorry⟩
noncomputable instance C.T2 : T2 C := ⟨add, sorry, sorry, sorry, sorry, sorry, sorry, sorry, sorry, sorry⟩

-- slow
example : @T1.toL9 _ C.T1 = @T2.toL9 _ C.T2 := rfl


import for_mathlib.algebra.homology.k_projective
import for_mathlib.category_theory.localization.derived_functor_triangulated
import category_theory.abelian.injective
import for_mathlib.algebra.homology.cochain_complex_opposites

noncomputable theory

open category_theory category_theory.category category_theory.limits
  category_theory.pretriangulated
open_locale zero_object

instance inverse_image_multiplicative {C D : Type*} [category C] [category D]
  (F : C ⥤ D) (W : morphism_property D)
  [W.multiplicative] : (W.inverse_image F).multiplicative :=
{ contains_identities := ⟨λ X, begin
    change W _,
    rw F.map_id,
    apply morphism_property.contains_identities.id W,
  end⟩,
  comp := (morphism_property.multiplicative.comp W).inverse_image F, }

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

namespace homological_complex

variables {ι : Type*} {c : complex_shape ι} (K L : homological_complex C c)

class is_K_injective : Prop :=
(null_homotopic : ∀ ⦃X : homological_complex C c⦄ (f : X ⟶ K)
  (hX : acyclic X), nonempty (homotopy f 0))

variables {K L}

lemma is_K_injective.of_homotopy_equiv [K.is_K_injective] (e : homotopy_equiv K L) :
  L.is_K_injective :=
⟨λ X f hX, begin
  obtain ⟨h⟩ := is_K_injective.null_homotopic (f ≫ e.inv) hX,
  refine ⟨(homotopy.of_eq (comp_id f)).symm.trans
    (((e.homotopy_inv_hom_id.symm.comp_left f).trans
      (homotopy.of_eq (assoc _ _ _).symm)).trans
        ((h.comp_right e.hom).trans (homotopy.of_eq zero_comp)))⟩,
end⟩

lemma is_K_injective.of_iso [K.is_K_injective] (e : K ≅ L) : L.is_K_injective :=
is_K_injective.of_homotopy_equiv (homotopy_equiv.of_iso e)

lemma is_K_injective.iff_of_iso (e : K ≅ L) :
  K.is_K_injective ↔ L.is_K_injective :=
begin
  split,
  { introI, exact is_K_injective.of_iso e, },
  { introI, exact is_K_injective.of_iso e.symm, },
end

lemma is_K_injective.of_is_zero (h : is_zero K) : K.is_K_injective :=
⟨λ X f hX, begin
  rw h.eq_of_tgt f 0,
  exact ⟨homotopy.refl _⟩
end⟩

instance zero_is_K_injective : is_K_injective (0 : homological_complex C c) :=
is_K_injective.of_is_zero (limits.is_zero_zero _)

end homological_complex

namespace cochain_complex

open homological_complex

variables (K : cochain_complex C ℤ)

lemma is_K_injective_iff : is_K_injective K ↔
  (homotopy_category.quotient _ _).obj K
    ∈ triangulated.right_orthogonal (homotopy_category.acyclic C) :=
begin
  split,
  { introI,
    rintros ⟨X⟩ f hX,
    obtain ⟨f, rfl⟩ := (homotopy_category.quotient _ _).map_surjective f,
    rw ← (homotopy_category.quotient C (complex_shape.up ℤ)).map_zero,
    refine homotopy_category.eq_of_homotopy _ _ (is_K_injective.null_homotopic _ _).some,
    erw homotopy_category.quotient_obj_mem_acyclic_iff at hX,
    exact hX, },
  { intro hK,
    refine ⟨λ X f hX, ⟨homotopy_category.homotopy_of_eq _ _ _⟩⟩,
    simp only [functor.map_zero],
    apply hK,
    simpa only [homotopy_category.quotient_obj_mem_acyclic_iff] using hX, },
end

lemma shift_is_K_injective_iff (K : cochain_complex C ℤ) (r : ℤ) :
  is_K_injective (K⟦r⟧) ↔ is_K_injective K :=
begin
  simp only [is_K_injective_iff],
  erw [set.respects_iso.mem_iff_of_iso (triangulated.right_orthogonal (homotopy_category.acyclic C))
   (((homotopy_category.quotient C (complex_shape.up ℤ)).comm_shift_iso r).app K),
   ← triangulated.is_triangulated_subcategory.shift_iff],
end

lemma is_K_injective_of_op (K : cochain_complex C ℤ)
  (hK : (op_equivalence.op_obj K).is_K_projective) :
  is_K_injective K :=
⟨λ (L : cochain_complex C ℤ) f hL, ⟨begin
  apply cochain_complex.unop_homotopy,
  let f' : op_equivalence.op_obj K ⟶ _ :=
    (cochain_complex.op_equivalence.functor C).map f.op,
  exact (is_K_projective.null_homotopic f' (cochain_complex.acyclic_op hL)).some,
end⟩⟩

lemma is_K_injective_of_bounded_below_of_injective
  (K : cochain_complex C ℤ) (n : ℤ) [K.is_strictly_ge n]
  [∀ (n : ℤ), injective (K.X n)] : is_K_injective K :=
begin
  haveI : K.is_strictly_ge (-(-n)),
  { simp only [neg_neg], apply_instance, },
  haveI : (op_equivalence.op_obj K).is_strictly_le (-n) := op_obj_is_strictly_le K (-n),
  haveI : ∀ (n : ℤ), projective ((op_equivalence.op_obj K).X n),
  { intro n,
    dsimp,
    apply_instance, },
  exact is_K_injective_of_op _ (is_K_projective_of_bounded_above_of_projective _ (-n)),
end

end cochain_complex

namespace homotopy_category

variables {C} {ι : Type*} {c : complex_shape ι}

class is_K_injective (K : homotopy_category C c) : Prop :=
(K_injective : K.as.is_K_injective)

lemma is_K_injective_iff' (K : homotopy_category C c) :
  K.is_K_injective ↔ K.as.is_K_injective :=
begin
  split,
  { exact λ h, h.K_injective, },
  { exact λ h, ⟨h⟩, },
end

lemma is_K_injective_iff (K : homotopy_category C (complex_shape.up ℤ)) : is_K_injective K ↔
  K ∈ triangulated.right_orthogonal (homotopy_category.acyclic C) :=
begin
  rw K.is_K_injective_iff',
  cases K,
  dsimp,
  apply cochain_complex.is_K_injective_iff,
end

variables (C c)

instance zero_is_K_injective :
  (0 : homotopy_category C c).is_K_injective :=
⟨⟨λ X f hf, ⟨begin
  have e : ∀ (X : homotopy_category C c), ((quotient C c).obj X.1 ≅ X),
  { rintro ⟨X⟩, exact (iso.refl _), },
  refine homotopy_of_eq _ _ (is_zero.eq_of_tgt (is_zero.of_iso (is_zero_zero _) (e _)) _ _),
end⟩⟩⟩

abbreviation K_injective := full_subcategory (λ (K : homotopy_category C c), K.is_K_injective)

instance is_K_injective_is_triangulated_subcategory :
  triangulated.is_triangulated_subcategory
    (λ (K : homotopy_category C (complex_shape.up ℤ)), K.is_K_injective) :=
begin
  convert (infer_instance : triangulated.is_triangulated_subcategory
      (triangulated.right_orthogonal (homotopy_category.acyclic C))),
  ext,
  exact is_K_injective_iff _,
end

instance K_injective_is_K_injective (K : K_injective C c) : K.obj.is_K_injective := K.2

--instance : pretriangulated (K_injective C (complex_shape.up ℤ)) := infer_instance

abbreviation K_injective.ι : K_injective C c ⥤ homotopy_category C c :=
full_subcategory_inclusion _

end homotopy_category

namespace derived_category

lemma Qh_map_bijective_of_is_K_injective
  (K L : homotopy_category C (complex_shape.up ℤ)) [L.is_K_injective] :
  function.bijective (λ (f : K ⟶ L), Qh.map f) :=
(triangulated.subcategory.right_orthogonal_bijective_Q_map
  (homotopy_category.acyclic C) _ _
  (by { rw ← L.is_K_injective_iff, apply_instance, }))

lemma Qh_map_bijective_of_is_K_injective'
  (K L : cochain_complex C ℤ) [L.is_K_injective] :
  function.bijective (λ (f : ((homotopy_category.quotient _ _).obj K ⟶
    (homotopy_category.quotient _ _).obj L)), Qh.map f) :=
(triangulated.subcategory.right_orthogonal_bijective_Q_map
  (homotopy_category.acyclic C) _ _
  ((cochain_complex.is_K_injective_iff L).1 infer_instance))

lemma Q_map_surjective_of_is_K_injective
  (K L : cochain_complex C ℤ) [L.is_K_injective] :
  function.surjective (λ (f : K ⟶ L), Q.map f) :=
λ f, begin
  obtain ⟨g, hg⟩ := (Qh_map_bijective_of_is_K_injective' K L).2 f,
  dsimp at hg,
  obtain ⟨g, rfl⟩ := (homotopy_category.quotient _ _).map_surjective g,
  exact ⟨g, hg⟩,
end

def homotopy_of_eq_Qh_map_eq_of_is_K_injective
  {K L : cochain_complex C ℤ} [L.is_K_injective] (f₁ f₂ : K ⟶ L)
  (h : Q.map f₁ = Q.map f₂) : homotopy f₁ f₂ :=
homotopy_category.homotopy_of_eq _ _ ((Qh_map_bijective_of_is_K_injective' K L).1 h)

end derived_category

namespace homotopy_category

variable (C)

namespace K_injective

def W : morphism_property (homotopy_category.K_injective C (complex_shape.up ℤ)) :=
(triangulated.subcategory.W (homotopy_category.acyclic C)).inverse_image (K_injective.ι _ _)

instance W_multiplicative : (W C).multiplicative :=
by { dsimp [W], apply_instance, }

variable {C}

def Φ : localizor_morphism (W C) (triangulated.subcategory.W (homotopy_category.acyclic C)) :=
{ functor := K_injective.ι _ _,
  mapW := λ X Y f hf, hf, }

instance Φ_functor_has_comm_shift :
  (Φ : localizor_morphism (W C) _).functor.has_comm_shift ℤ :=
by { dsimp only [Φ], apply_instance, }

instance Φ_functor_is_triangulated :
  (Φ : localizor_morphism (W C) _).functor.is_triangulated :=
by { dsimp only [Φ], apply_instance, }

end K_injective

end homotopy_category

namespace category_theory

variable (C)

include C

class has_enough_K_injectives : Prop :=
(condition : ∀ (K : homotopy_category C (complex_shape.up ℤ)),
  nonempty (homotopy_category.K_injective.Φ.right_resolution K))

end category_theory

open category_theory

namespace homotopy_category

namespace K_injective

variable {C}

def Qh : K_injective C (complex_shape.up ℤ) ⥤ derived_category C :=
K_injective.ι _ _ ⋙ derived_category.Qh

instance full_Qh : full (Qh : _ ⥤ derived_category C) :=
functor.full_of_surjective _ (λ K L, (derived_category.Qh_map_bijective_of_is_K_injective _ _).2)

instance faithful_Qh : faithful (Qh : _ ⥤ derived_category C) :=
⟨λ K L, (derived_category.Qh_map_bijective_of_is_K_injective _ _).1⟩

variable (C)

lemma W_eq_isomorphisms : W C = morphism_property.isomorphisms _ :=
begin
  ext K L f,
  split,
  { intro hf,
    haveI : is_iso (Qh.map f) := (triangulated.subcategory.is_iso_map_iff
      (acyclic C) derived_category.Qh f).2 hf,
    exact is_iso_of_reflects_iso f Qh, },
  { rintro (h : is_iso _),
    haveI := h,
    exact (triangulated.subcategory.is_iso_map_iff (acyclic C) derived_category.Qh ((ι _ _).map f)).1
      infer_instance, },
end

variable {C}

lemma W_inverts {D : Type*} [category D]
  (G : K_injective C (complex_shape.up ℤ) ⥤ D) :
  (W C).is_inverted_by G :=
begin
  intros X Y f hf,
  haveI : is_iso f := by simpa only [W_eq_isomorphisms] using hf,
  apply_instance,
end

variables [has_enough_K_injectives C]

instance (Y : homotopy_category C (complex_shape.up ℤ)) :
  nonempty (Φ.right_resolution Y) :=
has_enough_K_injectives.condition Y

instance (Y : homotopy_category C (complex_shape.up ℤ)) (X : Φ.right_resolution Y) :
  is_iso (derived_category.Qh.map X.hom.f) :=
by simpa only [triangulated.subcategory.is_iso_map_iff (homotopy_category.acyclic C)
  derived_category.Qh] using X.hom.hf

instance ess_surj_Qh : ess_surj (Qh : _ ⥤ derived_category C) :=
⟨λ Z, begin
  have e := derived_category.Qh.obj_obj_preimage_iso Z,
  let Y := derived_category.Qh.obj_preimage Z,
  let X := (has_enough_K_injectives.condition Y).some,
  exact ⟨X.right.obj, ⟨(as_iso (derived_category.Qh.map X.hom.f)).symm ≪≫
    derived_category.Qh.obj_obj_preimage_iso Z⟩⟩,
end⟩

instance : is_equivalence (Qh : _ ⥤ derived_category C) :=
equivalence.of_fully_faithfully_ess_surj _

instance Qh_is_localization : Qh.is_localization (W C) :=
begin
  haveI : (𝟭 _).is_localization (W C),
  { refine functor.is_localization.for_id _ _,
    rw W_eq_isomorphisms, },
  exact functor.is_localization.of_equivalence_target (𝟭 _) (W C) Qh
    (functor.as_equivalence Qh) (functor.left_unitor _),
end

instance Φ_induced_functor_obj_is_K_injective (Y : homotopy_category C (complex_shape.up ℤ))
  (X : Φ.right_resolution Y) : (Φ.induced_functor.obj X.right).obj.is_K_injective :=
X.right.obj.2

instance Φ_induced_functor_obj_is_K_injective' (Y : homotopy_category C (complex_shape.up ℤ))
  (X : Φ.right_resolution Y) : (Φ.functor.obj X.right.obj).is_K_injective :=
X.right.obj.2

lemma lift_map {Y₁ Y₂ : homotopy_category C (complex_shape.up ℤ)} (f : Y₁ ⟶ Y₂)
  (X₁ : Φ.right_resolution Y₁) (X₂ : Φ.right_resolution Y₂) :
  ∃ (f' : X₁.right.obj ⟶ X₂.right.obj), X₁.hom.f ≫ Φ.functor.map f' = f ≫ X₂.hom.f :=
begin
  let f'' := inv (derived_category.Qh.map (X₁.hom.f)) ≫
    derived_category.Qh.map (f ≫ X₂.hom.f),
  obtain ⟨f', hf'⟩ := (derived_category.Qh_map_bijective_of_is_K_injective _ _).2 f'',
  refine ⟨f', (derived_category.Qh_map_bijective_of_is_K_injective _ _).1 _⟩,
  dsimp [Φ] at hf' ⊢,
  simp only [functor.map_comp, hf', f'', is_iso.hom_inv_id_assoc],
end

instance (Y : homotopy_category C (complex_shape.up ℤ)) :
  is_preconnected' (Φ.right_resolution Y) :=
⟨⟨begin
  rintro ⟨X₁⟩ ⟨X₂⟩,
  obtain ⟨g, hg⟩ := K_injective.lift_map (𝟙 Y) X₁ X₂,
  dsimp at hg,
  rw id_comp at hg,
  refine quot.sound ⟨structured_arrow.hom_mk ⟨g, _⟩ _⟩,
  { change (triangulated.subcategory.W (homotopy_category.acyclic C)) _,
    rw ← triangulated.subcategory.is_iso_map_iff (homotopy_category.acyclic C)
      derived_category.Qh,
    replace hg := derived_category.Qh.congr_map hg,
    rw functor.map_comp at hg,
    exact is_iso.of_is_iso_fac_left hg, },
  { ext, exact hg, },
end⟩⟩

instance Φ_is_localization_equivalence : (Φ : localizor_morphism (W C) _).is_localization_equivalence :=
begin
  rw localizor_morphism.is_localization_equivalence.iff_is_localization Φ
    (derived_category.Qh : _ ⥤ derived_category C),
  change Qh.is_localization _,
  apply_instance,
end

lemma right_derivability_structure :
  right_derivability_structure.basic (Φ : localizor_morphism (W C) _) :=
{ right_resolution_connected := λ Y, { },
  nonempty_arrow_right_resolution := λ Y₁ Y₂ f, begin
    let X₁ := (has_enough_K_injectives.condition Y₁).some,
    let X₂ := (has_enough_K_injectives.condition Y₂).some,
    obtain ⟨f', fac⟩ := K_injective.lift_map f X₁ X₂,
    exact ⟨X₁, X₂, f', fac⟩,
  end, }

instance Φ_functor_comp_Qh_ess_surj_on_dist_triang : (Φ.functor ⋙
  derived_category.Qh : _ ⥤ derived_category C).ess_surj_on_dist_triang :=
K_injective.right_derivability_structure.Φ_functor_comp_L_ess_surj_on_dist_triang _

section

variables {D : Type*} [category D]
  (F : homotopy_category C (complex_shape.up ℤ) ⥤ D)

instance existence_right_derived_functor :
  F.has_right_derived_functor (triangulated.subcategory.W (acyclic C)) :=
right_derivability_structure.basic.existence_derived_functor
  K_injective.right_derivability_structure F (W_inverts _)

lemma is_iso_app (RF : derived_category C ⥤ D)
  (α : F ⟶ derived_category.Qh ⋙ RF)
  [RF.is_right_derived_functor α]
  (K : homotopy_category C (complex_shape.up ℤ)) [K.is_K_injective] :
  is_iso (α.app K) :=
right_derivability_structure.basic.is_iso_app
  K_injective.right_derivability_structure derived_category.Qh F (W_inverts _)
  RF α ⟨K, infer_instance⟩

instance (K : homotopy_category C (complex_shape.up ℤ)) [K.is_K_injective] :
  is_iso ((F.right_derived_functor_α derived_category.Qh
    (triangulated.subcategory.W (acyclic C))).app K) :=
is_iso_app _ _ _ _

section

variables [has_zero_object D] [has_shift D ℤ] [preadditive D]
  [∀ (n : ℤ), (shift_functor D n).additive] [pretriangulated D]
  [F.has_comm_shift ℤ] [functor.is_triangulated F]

instance right_derived_functor_is_triangulated :
  (F.right_derived_functor derived_category.Qh
    (triangulated.subcategory.W (acyclic C))).is_triangulated :=
right_derivability_structure.basic.derived_functor_is_triangulated'
    K_injective.right_derivability_structure F derived_category.Qh (W_inverts _)

end

end

end K_injective

end homotopy_category


-- Copyright (c) 2017 Scott Morrison. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.
-- Authors: Stephen Morgan, Scott Morrison
import .pentagon_in_terms_of_natural_transformations_definitions
import tidy.its

open categories
open categories.functor
open categories.products
open categories.natural_transformation

namespace categories.monoidal_category

universe variables u v

variables (C : Type u) [𝒞 : monoidal_category.{u v} C]
include 𝒞

local attribute [tidy] dsimp_all'

set_option trace.check true

-- TODO tidy this up
lemma pentagon_in_terms_of_natural_transformations :
  pentagon_3step C = pentagon_2step C :=
  begin
    dsimp',
    apply NaturalTransformations_componentwise_equal,
    intros WXYZ,
    induction WXYZ with WXY Z,
    induction WXY with WX Y,
    induction WX with W X,
    {
      tidy,
      -- erw rewrite_tensor_as_otimes, -- FIXME terrifying: equalities between objects are evil, and hence rewriting along them is hard
      have p := monoidal_category.pentagon C W X Y Z,
      -- have p := monoidal_category.pentagon C X_fst_fst_fst X_fst_fst_snd X_fst_snd X_snd,
      obviously, -- FIXME
    },
end

end categories.monoidal_category


import for_mathlib.endomorphisms.basic
import for_mathlib.exact_functor

universe v

namespace category_theory

namespace endomorphisms

open homological_complex category_theory category_theory.limits category

variables (𝓐 : Type*) [category.{v} 𝓐]

@[simps]
def tautological_nat_trans :
  (endomorphisms.forget 𝓐) ⟶ (endomorphisms.forget 𝓐) :=
{ app := λ X, X.e, }

variable {𝓐}

variables [abelian 𝓐]
  [has_coproducts_of_shape (ulift.{v} ℕ) 𝓐] [has_products_of_shape (ulift.{v} ℕ) 𝓐]
variables {M : Type*} {c : complex_shape M} (F : endomorphisms 𝓐 ⥤ homological_complex 𝓐 c)
variables (Y : homological_complex (endomorphisms 𝓐) c)

@[simps]
def _root_.homological_complex.tautological_endomorphism : Y ⟶ Y :=
{ f := λ i, ⟨(Y.X i).e, rfl⟩, }

lemma homology_functor_obj_e (i : M) :
  ((homology_functor (endomorphisms 𝓐) c i).obj Y).e =
    ((homology_functor (endomorphisms 𝓐) c i).map Y.tautological_endomorphism).f  :=
begin
  have h₁ := ((endomorphisms.forget 𝓐).homology_functor_iso c i).hom.naturality
    Y.tautological_endomorphism,
  rw [← cancel_mono (((endomorphisms.forget 𝓐).homology_functor_iso c i).inv.app Y),
    assoc] at h₁,
  conv_lhs at h₁ { congr, skip, rw [← nat_trans.comp_app, iso.hom_inv_id, nat_trans.id_app], },
  rw comp_id at h₁,
  conv_lhs at h₁ { dsimp only [functor.comp, endomorphisms.forget], },
  rw h₁,
  clear h₁,
  have h₂ := nat_trans.congr_app (functor.naturality_homology_functor_iso
    (tautological_nat_trans 𝓐) c i) Y,
  dsimp [nat_trans.hcomp] at h₂,
  rw [comp_id, id_comp, ← cancel_mono
    (((endomorphisms.forget 𝓐).homology_functor_iso c i).inv.app Y), assoc] at h₂,
  conv_lhs at h₂ { congr, skip, rw [← nat_trans.comp_app, iso.hom_inv_id, nat_trans.id_app], },
  erw comp_id at h₂,
  exact h₂,
end

end endomorphisms

end category_theory