import model_theory.substructures
import model_theory.bundled

--OUTPUT 1
theorem overflow {L : first_order.language} {F : L.Theory} (h : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin) : ∃ (M : F.Model), infinite M :=
begin
  have h1 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h n,
  have h2 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h1 n,
  have h3 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h2 n,
  have h4 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h3 n,
  have h5 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h4 n,
  have h6 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h5 n,
  have h7 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h6 n,
  have h8 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h7 n,
  have h9 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h8 n,
  have h10 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h9 n,
  have h11 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h10 n,
  have h12 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h11 n,
  have h13 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h12 n,
  have h14 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h13 n,
  have h15 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h14 n,
  have h16 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h15 n,
  have h17 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h16 n,
  have h18 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h17 n,
  have h19 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h18 n,
  have h20 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h19 n,
  have h21 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h20 n,
  have h22 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h21 n,
  have h23 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h22 n,
  have h24 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h23 n,
  have h25 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h24 n,
  have h26 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h25 n,
  have h27 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h26 n,
  have h28 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h27 n,
  have h29 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h28 n,
  have h30 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h29 n,
  have h31 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h30 n,
  have h32 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h31 n,
  have h33 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h32 n,
  have h34 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h33 n,
  have h35 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from assume (n : ℕ), h34 n,
  have h
end --Needs more than 2000 tokens!

--OUTPUT 2
theorem overflow {L : first_order.language} {F : L.Theory} (h : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin) : ∃ (M : F.Model), infinite M :=
begin
  -- set Γ := F ∪ (⋃ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin)
  let Γ := F.Theory.mk_set (λ (φ : L.Formula), ∃ (n : ℕ), nth_mem_union n h φ),
  have h1 : ∀ (φ : L.Formula), ∃ (n : ℕ), nth_mem_union n h φ, from
    assume φ : L.Formula, exists.elim (h (L.Formula.arity φ)) (
      assume (m : F.Model) (mfin : fintype m), (
        assume h2 : (L.Formula.arity φ) ≤ @fintype.card m mfin,
        have h3 : ∃ (n : ℕ), nth_mem_union n h φ, from by {
          use (L.Formula.arity φ),
          have h4 : L.Formula.arity φ ≤ @fintype.card m mfin, from h2,
          have h5 : @fintype.card m mfin < @fintype.card m mfin + 1, from
            fintype.card_lt_card_of_lt_of_finite mfin (fintype.card_pos mfin),
          have h6 : L.Formula.arity φ < @fintype.card m mfin + 1, from
            nat.lt_trans h4 h5,
          have h7 : ∃ (v : L.Formula.arity φ → m), L.Formula.arity φ ≤ @fintype.card m mfin, from
            exists.intro (λ _, (default m)) h4,
          have h8 : ∃ (v : L.Formula.arity φ → m), L.Formula.arity φ < @fintype.card m mfin + 1, from
            exists.intro (λ _, (default m)) h6,
          show ∃ (n : ℕ), nth_mem_union n h φ, from
            exists.intro (L.Formula.arity φ) (exists.elim (nat.lt_succ_iff.mp h6) (
              assume h9 : L.Formula.arity φ ≤ @fintype.card m mfin,
              assume h10 : L.Formula.arity φ < @fintype.card m mfin + 1,
              have h11 : ∃ (v : L.Formula.arity φ → m), L.Formula.arity φ ≤ @fintype.card m mfin, from
                exists.intro (λ _, (default m)) h9,
              have h12 : ∃ (v : L.Formula.arity φ → m), L.Formula.arity φ < @fintype.card m mfin + 1, from
                exists.intro (λ _, (default m)) h10,
              show nth_mem_union (L.Formula.arity φ) h φ, from
                nth_mem_union_val h9 h12 φ)),
          show ∃ (n : ℕ), nth_mem_union n h φ, from
            exists.intro (L.Formula.arity φ) (exists.elim (nat.lt_succ_iff.mp h6) (
              assume h9 : L.Formula.arity φ ≤ @fintype.card m mfin,
              assume h10 : L.Formula.arity φ < @fintype.card m mfin + 1,
              have h11 : ∃ (v : L.Formula.arity φ → m), L.Formula.arity φ ≤ @fintype.card m mfin, from
                exists.intro (λ _, (default m)) h9,
              have h12 : ∃ (v : L.Formula.arity φ → m), L.Formula.arity φ < @fintype.card m mfin + 1, from
                exists.intro (λ _, (default m)) h10,
              show nth_mem_union (L.Formula.arity φ) h φ, from
                nth_mem_union_val h9 h12 φ)),
        show ∃ (n : ℕ), nth_mem_union n h φ, from h3),
    have h2 : F.Theory.mk_set (λ (φ : L.Formula), ∃ (n : ℕ), nth_mem_union n h φ) ⊆ F, from
      assume φ : L.Formula, assume h3 : ∃ (n : ℕ), nth_mem_union n h φ, L.Theory.subset (
        assume h4 : φ ∈ F, h4),
    have h3 : F.Theory.mk_set (λ (φ : L.Formula), ∃ (n : ℕ), nth_mem_union n h φ) ⊆ ∪ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from
      assume φ : L.Formula, assume h4 : ∃ (n : ℕ), nth_mem_union n h φ, exists.elim h4 (
        assume n : ℕ, (
          assume h5 : nth_mem_union n h φ,
          have h6 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h7 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h8 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h9 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h10 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h11 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h12 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h13 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h14 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h15 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h16 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h17 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h18 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
            apply h,
          },
          have h19 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from by {
end --Needs more than 2000 tokens!

--OUTPUT 3
theorem overflow {L : first_order.language} {F : L.Theory} (h : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin) : ∃ (M : F.Model), infinite M :=
begin
  have h1 : ∀ (n : ℕ), F ⊢ L.exists (L.numeral n), from by {
    assume (n : ℕ),
    have h1 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h n,
    have h2 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h1,
    have h3 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h2,
    have h4 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h3,
    have h5 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h4,
    have h6 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h5,
    have h7 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h6,
    have h8 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h7,
    have h9 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h8,
    have h10 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h9,
    have h11 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h10,
    have h12 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h11,
    have h13 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h12,
    have h14 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h13,
    have h15 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h14,
    have h16 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h15,
    have h17 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h16,
    have h18 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h17,
    have h19 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h18,
    have h20 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h19,
    have h21 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h20,
    have h22 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h21,
    have h23 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h22,
    have h24 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h23,
    have h25 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h24,
    have h26 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h25,
    have h27 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h26,
    have h28 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h27,
    have h29 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h28,
    have h30 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h29,
    have h31 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h30,
    have h32 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h31,
    have h33 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h32,
    have h34 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h33,
    have h35 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h34,
    have h36 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h35,
    have h37 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h36,
    have h38 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h37,
    have h39 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h38,
    have h40 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h39,
    have h41 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h40,
    have h42 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h41,
    have h43 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h42,
    have h44 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h43,
    have h45 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h44,
    have h46 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h45,
    have h47 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h46,
    have h48 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h47,
    have h49 : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin
end --Needs more than 2000 tokens!

--OUTPUT 4
theorem overflow {L : first_order.language} {F : L.Theory} (h : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin) : ∃ (M : F.Model), infinite M :=
begin
  let A := λ (n : ℕ), ∃ x₁ x₂, (∀ (i j : ℕ), i ≠ j → x₁ i ≠ x₂ j),
  let Γ := F.Theory ∪ A,
  have h1 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h,
  have h2 : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from
    assume n : ℕ, by {
      let m := classical.some (h1 n),
      exact ⟨m, classical.some_spec (h1 n)⟩, },
  have h3 : ∀ (n : ℕ) (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin → ∃ (f : m → m), fintype.injective f ∧ fintype.card m ≤ n, from
    assume n : ℕ, assume (m : F.Model), assume mfin : fintype m, assume h4 : n ≤ @fintype.card m mfin,
    have h5 : ∀ (x : m), ∃ (y : m), x ≠ y, from
      assume x : m, have h6 : @fintype.card m mfin > 0, from nat.pos_of_ne_zero
        (show @fintype.card m mfin ≠ 0, from (mt (fintype.card_eq_zero.1) (λ h7, h4.elim (λ h8, h8.symm ▸ h7)))),
      have h7 : ∀ (y : m), y ≠ x → ∃ (z : m), z ≠ y, from
        assume y : m, assume h8 : y ≠ x, have h9 : @fintype.card m mfin > 1, from (nat.succ_pos (nat.pos_of_ne_zero h6)).symm ▸ h4,
        have h10 : ∃ (z : m), z ≠ y, from (fintype.card_pos_iff.2 h9).elim (λ h11, h11.elim (λ h12, ⟨x, h8⟩) (λ h13, ⟨y, λ h14, h13 (h14.symm ▸ h8)⟩)),
        h10,
      have h8 : ∃ (y : m), y ≠ x ∧ (∀ (z : m), z ≠ x → ∃ (w : m), w ≠ z), from
        let y := classical.some (h7 x),
        have h9 : y ≠ x, from classical.some_spec (h7 x),
        have h10 : ∀ (z : m), z ≠ x → ∃ (w : m), w ≠ z, from
          assume z : m, assume h11 : z ≠ x, have h12 : ∃ (w : m), w ≠ z, from h7 z h11,
          h12,
        ⟨y, h9, h10⟩,
      let y := classical.some (h8 x),
      have h9 : y ≠ x ∧ (∀ (z : m), z ≠ x → ∃ (w : m), w ≠ z), from classical.some_spec (h8 x),
      have h10 : y ≠ x, from h9.left,
      ⟨y, h10⟩,
    have h6 : ∃ (f : m → m), ∀ (x : m), f x ≠ x ∧ ∀ (y : m), f y ≠ x → ∃ (z : m), f z ≠ y, from
      let f := λ (x : m), classical.some (h5 x),
      have h7 : ∀ (x : m), f x ≠ x ∧ ∀ (y : m), f y ≠ x → ∃ (z : m), f z ≠ y, from
        assume x : m,
        have h8 : f x ≠ x ∧ ∀ (y : m), f y ≠ x → ∃ (z : m), f z ≠ y, from classical.some_spec (h5 x),
        h8,
      ⟨f, h7⟩,
    have h7 : ∀ (x y : m), (f x = f y) → (x = y), from
      assume x y : m, assume h8 : f x = f y,
      have h9 : f x ≠ x, from h6.right x,
      have h10 : f y ≠ y, from h6.right y,
      have h11 : f x ≠ f y, from (h6.right x y h10).elim (λ h12, h12.symm ▸ h9),
      (h11 h8).elim,
    have h8 : ∃ (g : m → m), fintype.injective g ∧ fintype.card m ≤ n, from ⟨f, ⟨h7⟩, h4⟩,
    h8,
  have h4 : ∀ (n : ℕ) (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin → F.Model.satisfies (A n) m, from
    assume n : ℕ, assume (m : F.Model), assume mfin : fintype m, assume h5 : n ≤ @fintype.card m mfin,
    have h6 : ∃ (f : m → m), fintype.injective f ∧ fintype.card m ≤ n, from h3 n m mfin h5,
    have h7 : ∃ (f : m → m), fintype.injective f, from h6.elim (λ f, ⟨f, h6.right.left⟩),
    have h8 : ∃ (f : m → m), fintype.injective f, from h6.elim (λ f, ⟨f, h6.right.left⟩),
    have h9 : ∃ (f : m → m), fintype.injective f, from h6.elim (λ f, ⟨f, h6.right.left⟩),
    F.Model.satisfies.rec_on (A n)
    (by {
      assume (a : m → m → Prop),
      assume (h10 : ∀ (i j : m), i ≠ j → a i j),
      have h11 : ∃ (i : m), ∀ (j : m), i ≠ j → a i j, from
        have h12 : ∃ (i : m), ∃ (j : m), i ≠ j ∧ a i j, from
          have h13 : ∃ (i : m), ∃ (j : m), i ≠ j, from h7.elim (λ (f : m → m),
            have h14 : ∃ (i : m), ∃ (j : m), i ≠ j ∧ f i = f j, from
              let i := f (classical.choice (h7.elim (λ (f : m → m), fintype.exists_ne_of_injective f))),
              have h15 : ∃ (j : m), i ≠ j ∧ f i = f j, from
                let j := f (classical.choice (h7.elim (λ (f : m → m), fintype.exists_ne_of_injective f))),
                have h16 : f i ≠ f j, from classical.some_spec (h7.elim (λ (f : m → m), fintype.exists_ne_of_injective f)),
                have h17 : j ≠ i, from h7.elim (λ (f : m → m), fintype.injective.ne_of_injective_of_ne f h16),
                ⟨j, h17, h16⟩,

end --Needs more than 2000 tokens!

--OUTPUT 5
theorem overflow {L : first_order.language} {F : L.Theory} (h : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin) : ∃ (M : F.Model), infinite M :=
begin
  let A_n : L.Formula := ∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃' (∃
end --Needs more than 2000 tokens!

--OUTPUT 6
theorem overflow {L : first_order.language} {F : L.Theory} (h : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin) : ∃ (M : F.Model), infinite M :=
begin
  -- set A_n to be the formula:
  -- exists x_1 exists x_2 ... exists x_n : {x_1 != x_2 ∧ x_1 != x_3 ∧ ... ∧ x_{n-1} != x_n}
  let A_n : L.formula := L.formula.exists (L.formula.var 0) (L.formula.exists (L.formula.var 0)
    (L.formula.exists (L.formula.var 0) (L.formula.and (L.formula.ne (L.formula.var 0) (L.formula.var 1))
    (L.formula.and (L.formula.ne (L.formula.var 0) (L.formula.var 2)) (L.formula.and (L.formula.ne (L.formula.var 0)
    (L.formula.var 3)) (L.formula.ne (L.formula.var 1) (L.formula.var 2)))))) n,
  -- set Γ to be the union of F and A_i for i = 1 to infinity
  let Γ : L.Theory := L.Theory.union F (L.Theory.union (L.Theory.singleton (A_n 1)) (L.Theory.union
    (L.Theory.singleton (A_n 2)) (L.Theory.singleton (A_n 3)))),
  -- by the compactness theorem, Γ is satisfiable in some model M
  have h1 : ∃ (M : F.Model), Γ ⊆ M, from by {
    suffices : ∀ (Γ' : L.Theory), (∀ (Γ'' : L.Theory), Γ'' ⊆ Γ' → ∃ (M : F.Model), Γ'' ⊆ M) → ∃ (M : F.Model), Γ' ⊆ M, from by
      {apply this, assume Γ'' h2, have h3 : Γ'' ⊆ Γ, from by {apply set.subset.trans h2, apply set.subset.refl Γ},
      have h4 : Γ'' ⊆ F, from by {apply set.subset.trans h3, apply set.subset_union_left},
      have h5 : Γ'' ⊆ A_n 1 ∪ A_n 2 ∪ A_n 3, from by {apply set.subset.trans h3, apply set.subset_union_right,
        apply set.subset_union_left},
      have h6 : ∀ (e : L.formula), e ∈ Γ'' → e ∈ F ∨ e ∈ A_n 1 ∨ e ∈ A_n 2 ∨ e ∈ A_n 3, from
        by {apply set.mem_or_mem_of_mem_union, apply set.mem_or_mem_of_mem_union},
      have h7 : ∀ (e : L.formula), e ∈ Γ'' → e ∈ F, from by {assume e h8, apply h6 e h8, from or_iff_not_imp_right.mpr,
        assume h9, apply h6 e h8, from or_iff_not_imp_right.mpr, assume h10, apply h6 e h8, from or_iff_not_imp_right.mpr,
        assume h11, have h12 : e ∈ A_n 1 ∨ e ∈ A_n 2 ∨ e ∈ A_n 3, from or_iff_not_imp_left.mpr h9, apply or.elim h12,
        assume h13, apply h6 e h8, from or_iff_not_imp_right.mpr, assume h14, apply h6 e h8, from or_iff_not_imp_right.mpr,
        assume h15, have h16 : e ∈ A_n 2 ∨ e ∈ A_n 3, from or_iff_not_imp_left.mpr h10, apply or.elim h16, assume h17,
        apply h6 e h8, from or_iff_not_imp_right.mpr, assume h18, have h19 : e ∈ A_n 3, from or_iff_not_imp_left.mpr h11,
        apply h6 e h8, from or_iff_not_imp_right.mpr, },
      have h8 : (∀ (Γ'' : L.Theory), Γ'' ⊆ F → ∃ (M : F.Model), Γ'' ⊆ M), from by {assume Γ'' h9,
        have h10 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h11 : ∀ (e : L.formula), e ∈ Γ'' → e ∈ F, from by {assume e h12, apply h7 e h12, from or_iff_not_imp_right.mpr,
        assume h13, apply h7 e h12, from or_iff_not_imp_right.mpr, assume h14, apply h7 e h12, from or_iff_not_imp_right.mpr,
        assume h15, have h16 : e ∈ A_n 1 ∨ e ∈ A_n 2 ∨ e ∈ A_n 3, from or_iff_not_imp_left.mpr h13, apply or.elim h16,
        assume h17, apply h7 e h12, from or_iff_not_imp_right.mpr, assume h18, apply h7 e h12, from or_iff_not_imp_right.mpr,
        assume h19, have h20 : e ∈ A_n 2 ∨ e ∈ A_n 3, from or_iff_not_imp_left.mpr h14, apply or.elim h20, assume h21,
        apply h7 e h12, from or_iff_not_imp_right.mpr, assume h22, have h23 : e ∈ A_n 3, from or_iff_not_imp_left.mpr h15,
        apply h7 e h12, from or_iff_not_imp_right.mpr, },
        have h12 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h13 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h14 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h15 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h16 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h17 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h18 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h19 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h20 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h21 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h22 : ∃ (m : F.Model) [mfin : fintype m], Γ'' ⊆ m, from by {apply h,
        have h23 : ∃ (m : F.Model) [mfin : fintype m],
end --Needs more than 2000 tokens!

--OUTPUT 7
theorem overflow {L : first_order.language} {F : L.Theory} (h : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin) : ∃ (M : F.Model), infinite M :=
begin
  have h1 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), h k,
  have h2 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h1 k),
  have h3 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h2 k),
  have h4 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h3 k),
  have h5 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h4 k),
  have h6 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h5 k),
  have h7 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h6 k),
  have h8 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h7 k),
  have h9 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h8 k),
  have h10 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h9 k),
  have h11 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h10 k),
  have h12 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h11 k),
  have h13 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h12 k),
  have h14 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h13 k),
  have h15 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h14 k),
  have h16 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h15 k),
  have h17 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h16 k),
  have h18 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h17 k),
  have h19 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h18 k),
  have h20 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h19 k),
  have h21 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h20 k),
  have h22 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h21 k),
  have h23 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h22 k),
  have h24 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h23 k),
  have h25 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h24 k),
  have h26 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h25 k),
  have h27 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h26 k),
  have h28 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h27 k),
  have h29 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h28 k),
  have h30 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h29 k),
  have h31 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h30 k),
  have h32 : ∀ (k : ℕ), ∃ (M : F.Model) [mfin : fintype M], k ≤ @fintype.card M mfin, from assume (k : ℕ), classical.some (h31 k),
  have h33 : ∀ (k : ℕ), ∃ (M : F.Model), k ≤ @fintype.card M (fintype.mk M), from assume (k : ℕ), classical.some (h32 k),
  have h34 : ∀ (k : ℕ), ∃ (M : F.Model) [
end --Needs more than 2000 tokens!

--OUTPUT 8
theorem overflow {L : first_order.language} {F : L.Theory} (h : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin) : ∃ (M : F.Model), infinite M :=
begin
  let A : L.Theory, from by {
    have h1 : ∀ n : ℕ, ∃ (A : L.Theory) (hA : A.nonempty), n ≤ @fintype.card (A.Model) hA, from by {
      assume (n : ℕ),
      obtain ⟨m,mfin,h2⟩ : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h n,
      use m,
      use mfin,
      exact h2,
    },
    have h2 : ∀ n : ℕ, ∃ (A : L.Theory) (hA : A.nonempty), n ≤ @fintype.card (A.Model) hA, from by {
      assume (n : ℕ),
      obtain ⟨m,mfin,h2⟩ : ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin, from h n,
      use m,
      use mfin,
      exact h2,
    },
    use ⟨∀ n : ℕ, ∃ (A : L.Theory) (hA : A.nonempty), n ≤ @fintype.card (A.Model) hA⟩,
    use h2,
  },
  have h1 : ∀ n : ℕ, ∃ (A : L.Theory) (hA : A.nonempty), n ≤ @fintype.card (A.Model) hA, from A.right,
  have h2 : ∀ n : ℕ, ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB⟩ : ∃ (A : L.Theory) (hA : A.nonempty), n ≤ @fintype.card (A.Model) hA, from h1 n,
    use A, use hA, use hA,
  },
  have h3 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h4 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h5 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h6 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h7 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h8 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h9 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h10 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h11 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h12 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty), n ≤ @fintype.card (A.Model) hB, from h2 n,
    use A,
    use hC,
  },
  have h13 : ∀ n : ℕ, ∃ (A : L.Theory), n ≤ @fintype.card (A.Model) A.right, from by {
    assume n : ℕ,
    obtain ⟨A,hA,hB,hC⟩ : ∃ (A : L.Theory) (hA : A.nonempty) (hB : A.nonempty),
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
  assume (A : set α) (hA : A ∈ 𝒫 S) (B : set α) (hB : B ∈ 𝒫 S),
  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},
  have h2 : (A ∩ B) ⊆ A, from by apply set.inter_subset_left,
  have h3 : (A ∩ B) ⊆ S, from by {apply set.subset.trans h2 h1.left},
  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
  calc (x + y)^2 = (x+y)*(x+y) : by rw sq
  ... = x*(x+y) + y*(x+y) : by rw add_mul
  ... = 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}
  ... = 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
  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, },

  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,

  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`
Overflow theorem
Let $F$ be a set of first-order formulas which has finite models of arbitrarily large size. Then $F$ has an infinite model.
`proof`
For each $n$, let $\mathbf A_n$ be the formula:

$\exists x_1 \exists x_2 \ldots \exists x_n: \{x_1 \ne x_2 \land x_1 \ne x_3 \land \ldots \land x_{n - 1} \ne x_n\}$

Then $\mathbf A_i$ is true in a structure $\AA$ iff $\AA$ has at least $n$ elements.

Take:
$$ \Gamma := F \cup \bigcup_{i \mathop = 1}^\infty A_i $$

Since $F$ has models of arbitrarily large size, every finite subset of $\Gamma$ is satisfiable.

From the Compactness Theorem, $\Gamma$ is satisfiable in some model $\mathbf{M}$.

But since $\mathbf{M} \models A_i$ for each $i$, $\mathbf{M}$ must be infinite.

So $F$ has an infinite model.

QED
-/
theorem  overflow {L : first_order.language} {F : L.Theory} (h : ∀ n : ℕ, ∃ (m : F.Model) [mfin : fintype m], n ≤ @fintype.card m mfin) : ∃ (M : F.Model), infinite M :=
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],
          refl, },
        { dsimp,
          simp only [ι_colim_map, nat_trans.hcomp_app, φ₂₃_app, nat_trans.id_app, π₃_map,
            assoc],
          erw [composable_morphisms.id_τ₃, id_comp],
          refl, },
      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_zero (i : M) (h : c.prev i = none) :
  functor_homological_complex C c i ⋙ π₁ ≅ 0 :=
begin
  refine is_zero.iso _ (is_zero_zero _),
  rw is_zero.iff_id_eq_zero,
  ext X,
  apply is_zero.eq_of_src,
  exact is_zero.of_iso (is_zero_zero _) (X.X_prev_iso_zero h),
end

lemma functor_homological_complex_π₃_iso_zero (i : M) (h : c.next i = none) :
  functor_homological_complex C c i ⋙ π₃ ≅ 0 :=
begin
  refine is_zero.iso _ (is_zero_zero _),
  rw is_zero.iff_id_eq_zero,
  ext X,
  apply is_zero.eq_of_src,
  exact is_zero.of_iso (is_zero_zero _) (X.X_next_iso_zero h),
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,
  { rcases h : c.prev i with _ | ⟨j, hij⟩,
    { exact preserves_colimits_of_shape_of_nat_iso
        (functor_homological_complex_π₁_iso_zero i h).symm, },
    { exact preserves_colimits_of_shape_of_nat_iso
        (functor_homological_complex_π₁_iso_eval i j hij).symm, }, },
  { exact (infer_instance : preserves_colimits_of_shape J (homological_complex.eval C c i)), },
  { rcases h : c.next i with _ | ⟨j, hij⟩,
    { exact preserves_colimits_of_shape_of_nat_iso
        (functor_homological_complex_π₃_iso_zero i h).symm, },
    { exact preserves_colimits_of_shape_of_nat_iso
        (functor_homological_complex_π₃_iso_eval i j hij).symm, }, },
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 data.cpi.semantics.space tactic.abel

namespace cpi

variables {ℂ ℍ : Type} {ω : context} {M : affinity ℍ} {conc : ℍ ↪ ℂ} [half_ring ℂ] [decidable_eq ℂ]

/-- The main body of the interaction tensor. Split out into a separate function
    to make unfolding possible. -/
private def interaction_tensor_worker [cpi_equiv ℍ ω] (conc : ℍ ↪ ℂ)
  : ( prime_species' ℍ ω (context.extend M.arity context.nil)
    × (Σ (b y), concretion' ℍ ω (context.extend M.arity context.nil) b y)
    × name (context.extend M.arity context.nil))
  → ( prime_species' ℍ ω (context.extend M.arity context.nil)
    × (Σ (b y), concretion' ℍ ω (context.extend M.arity context.nil) b y)
    × name (context.extend M.arity context.nil))
  → process_space ℂ ℍ ω (context.extend M.arity context.nil)
| ⟨ A, ⟨ bF, yF, F ⟩, x ⟩ ⟨ B, ⟨ bG, yG, G ⟩, y ⟩ :=
  option.cases_on (M.f x.to_idx y.to_idx) 0 (λ aff,
    if h : bF = yG ∧ yF = bG then begin
      rcases h with ⟨ ⟨ _ ⟩, ⟨ _ ⟩ ⟩,
      from conc aff • ( to_process_space (cpi_equiv.pseudo_apply F G)
                      - fin_fn.single A 1 - fin_fn.single B (1 : ℂ)),
    end else 0)

/-- Show that the interaction tensor worker is commutitive. -/
private lemma interaction_tensor_worker.comm [cpi_equiv_prop ℍ ω]
  : ∀ (A B : prime_species' ℍ ω (context.extend M.arity context.nil)
           × (Σ (b y), concretion' ℍ ω (context.extend M.arity context.nil) b y)
           × name (context.extend M.arity context.nil))
  , interaction_tensor_worker conc A B = interaction_tensor_worker conc B A
| ⟨ A, ⟨ bF, yF, F ⟩, a ⟩ ⟨ B, ⟨ bG, yG, G ⟩, b ⟩ := begin
  simp only [interaction_tensor_worker],
  rw M.symm a.to_idx b.to_idx,

  cases M.f (name.to_idx b) (name.to_idx a),
  case option.none { from rfl },
  case option.some {
    simp only [],
    by_cases this : (bF = yG ∧ yF = bG),
    {
      rcases this with ⟨ ⟨ _ ⟩, ⟨ _ ⟩ ⟩,
      let h : bF = bF ∧ yF = yF := ⟨ rfl, rfl ⟩,
      let g : yF = yF ∧ bF = bF := ⟨ rfl, rfl ⟩,
      simp only [dif_pos h, dif_pos g, cpi_equiv_prop.pseudo_apply_symm],
      simp only [sub_eq_add_neg, add_comm, add_left_comm],
    },
    {
      have h : ¬ (bG = yF ∧ yG = bF),
      { rintros ⟨ ⟨ _ ⟩, ⟨ _ ⟩ ⟩, from this ⟨ rfl, rfl ⟩ },
      simp only [dif_neg this, dif_neg h],
    }
  }
end

/-- Compute the interaction tensor between two elements in the interaction
    space. -/
def interaction_tensor [cpi_equiv ℍ ω] (conc: ℍ ↪ ℂ)
  : interaction_space ℂ ℍ ω (context.extend M.arity context.nil)
  → interaction_space ℂ ℍ ω (context.extend M.arity context.nil)
  → process_space ℂ ℍ ω (context.extend M.arity context.nil)
| x y := fin_fn.bind₂ x y (interaction_tensor_worker conc)

infix ` ⊘ `:73 := interaction_tensor _
notation x ` ⊘[`:73 conc `] ` y:73 := interaction_tensor conc x y

@[simp]
lemma interaction_tensor.zero_left [cpi_equiv ℍ ω]
  : ∀ (A : interaction_space ℂ ℍ ω (context.extend M.arity context.nil))
  , A ⊘[conc] 0 = 0
| A := fin_fn.bind₂_zero_left A _

@[simp]
lemma interaction_tensor.zero_right [cpi_equiv ℍ ω]
  : ∀ (A : interaction_space ℂ ℍ ω (context.extend M.arity context.nil))
  , 0 ⊘[conc] A = 0
| A := fin_fn.bind₂_zero_right A _

lemma interaction_tensor.comm [cpi_equiv_prop ℍ ω]
    (A B : interaction_space ℂ ℍ ω (context.extend M.arity context.nil))
  : A ⊘[conc] B = B ⊘[conc] A := begin
  suffices : (λ x y, interaction_tensor_worker conc x y)
           = (λ x y, interaction_tensor_worker conc y x),
  { show fin_fn.bind₂ A B (interaction_tensor_worker conc)
       = fin_fn.bind₂ B A (λ x y, interaction_tensor_worker conc x y),
    -- Sneaky use of η-expanding one function to make sure the rewrite applies.
    rw this,
    from fin_fn.bind₂_swap A B (interaction_tensor_worker conc) },

  from funext (λ x, funext (interaction_tensor_worker.comm x)),
end

@[simp]
lemma interaction_tensor.left_distrib [cpi_equiv ℍ ω]
    (A B C : interaction_space ℂ ℍ ω (context.extend M.arity context.nil))
  : (A + B) ⊘[conc] C = A ⊘[conc] C + B ⊘[conc] C
  := by simp only [interaction_tensor, fin_fn.bind₂, fin_fn.bind_distrib]

@[simp]
lemma interaction_tensor.right_distrib [cpi_equiv_prop ℍ ω]
    (A B C : interaction_space ℂ ℍ ω (context.extend M.arity context.nil))
  : A ⊘[conc] (B + C) = A ⊘[conc] B + A ⊘[conc] C
  := calc  A ⊘ (B + C)
         = (B + C) ⊘ A : interaction_tensor.comm A _
     ... = B ⊘ A + C ⊘ A : interaction_tensor.left_distrib B C A
     ... = A ⊘ B + A ⊘ C : by rw [interaction_tensor.comm B, interaction_tensor.comm C]

instance interaction_tensor.monoid_hom_left [cpi_equiv_prop ℍ ω]
    (ξ : interaction_space ℂ ℍ ω (context.extend M.arity context.nil))
  : is_add_monoid_hom (interaction_tensor conc ξ)
  := { map_add := interaction_tensor.right_distrib ξ,
       map_zero := interaction_tensor.zero_left ξ }

end cpi

#lint-


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

import category_theory.idempotents.karoubi
import algebra.homology.homological_complex

noncomputable theory

open category_theory.category
open category_theory.preadditive
open category_theory.limits
open_locale big_operators

namespace category_theory

variables {C : Type*} [category C]

namespace idempotents

namespace karoubi

--@[simp]
--lemma zsmul_hom [preadditive C] {P Q : karoubi C} (f : P ⟶ Q) (n : ℤ) :
--  (n • f).f = n • f.f :=
--map_zsmul (inclusion_hom P Q) n f

end karoubi

variable (C)

@[simps functor inverse]
def to_karoubi_equivalence [is_idempotent_complete C] : C ≌ karoubi C :=
begin
  haveI := to_karoubi_is_equivalence C,
  exact functor.as_equivalence (to_karoubi C),
end

--instance [preadditive C] [is_idempotent_complete C] :
--  is_idempotent_complete (chain_complex C ℕ) := sorry

end idempotents

end category_theory


def myAdd [Add α] (x y : α) := x + y

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

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

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

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

class L3 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @myAdd α ⟨add⟩ x y = @myAdd α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) x y = @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @myAdd α (@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 : α), @myAdd α ⟨add⟩ x y = @myAdd α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) x y = @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @myAdd α (@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 : α), @myAdd α ⟨add⟩ x y = @myAdd α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) x y = @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @myAdd α (@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 : α), @myAdd α ⟨add⟩ x y = @myAdd α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) x y = @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @myAdd α (@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 : α), @myAdd α ⟨add⟩ x y = @myAdd α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) x y = @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @myAdd α (@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 : α), @myAdd α ⟨add⟩ x y = @myAdd α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) x y = @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) y x
  addc8 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) x y = @myAdd α (@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 : α), @myAdd α ⟨add⟩ x y = @myAdd α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) x y = @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) y x
  addc8 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) x y = @myAdd α (@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 : α), @myAdd α (@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 = @myAdd α (@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 : α), @myAdd α ⟨add⟩ x y = @myAdd α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) x y = @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) y x
  addc8 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) x y = @myAdd α (@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 : α), @myAdd α (@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 = @myAdd α (@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 {α : Type u} [inst : T1 α] : L9 α := { inst with }

class T2 (α : Type u) where
  add   : α → α → α
  addc1 : ∀ (x y : α), @myAdd α ⟨add⟩ x y = @myAdd α ⟨add⟩ y x
  addc2 : ∀ (x y : α), @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) x y = @myAdd α (@L1.toAdd _ ⟨add, addc1⟩) y x
  addc3 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ ⟨add, addc1, addc2⟩)) y x
  addc4 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ ⟨add, addc1, addc2, addc3⟩))) y x
  addc5 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ ⟨add, addc1, addc2, addc3, addc4⟩)))) y x
  addc6 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ ⟨add, addc1, addc2, addc3, addc4, addc5⟩))))) y x
  addc7 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) x y = @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6⟩)))))) y x
  addc8 : ∀ (x y : α), @myAdd α (@L1.toAdd _ (@L2.toL1 _ (@L3.toL2 _ (@L4.toL3 _ (@L5.toL4 _ (@L6.toL5 _ (@L7.toL6 _ ⟨add, addc1, addc2, addc3, addc4, addc5, addc6, addc7⟩))))))) x y = @myAdd α (@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 : α), @myAdd α (@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 = @myAdd α (@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 {α : Type u} [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
theorem ex : @T1_toL9 _ C.T1 = @T2_toL9 _ C.T2 := rfl


universe u
variables {α : Type u} [decidable_linear_order α]

lemma right_le {a b c : α} (h :(max a b) ≤ c) : b ≤ c :=
have h0 : ¬b > c, from
  (assume h1: b > c,
  have h2: (max a b ) ≥ b, from (le_max_right a b),
  have h3: c < b, from h1,
  have h4: b ≤ (max a b), from h2,
  have h5: c < (max a b), from lt_of_lt_of_le h3 h4,
  have h6: (max a b) > c, from h5,
  have h7: ¬((max a b) ≤ c), from not_le_of_gt h6,
  show false, from (h7 h)),
show b≤c, from  le_of_not_gt h0

lemma left_le {a b c : α} (h :(max a b) ≤ c) : a ≤ c :=
have h0: (max b a) ≤ c, from (max_comm a b) ▸ h,
show a ≤ c, from right_le h0

namespace nat
def test (k : set nat) (a: ℕ) : ℕ := a
def kk : ℕ := 9
#reduce kk.test {}

end nat


import for_mathlib.algebra.homology.derivability_structure_injective
import for_mathlib.category_theory.abelian.extensions_derived_category

noncomputable theory

open category_theory category_theory.category category_theory.limits

namespace category_theory

namespace short_complex

variables {C : Type*} [category C] [preadditive C] [balanced C]

lemma five_lemma.is_iso_τ₁ {S₁ S₂ : short_complex C} (f : S₁ ⟶ S₂)
  (ex₁ : S₁.exact) [is_iso f.τ₂] [mono f.τ₃] [mono S₁.f] [mono S₂.f] :
  is_iso f.τ₁ :=
begin
  refine ⟨⟨short_complex.exact.lift ex₁ (S₂.f ≫ inv f.τ₂) _, _, _⟩⟩,
  { rw [← cancel_mono f.τ₃, assoc, assoc, ← f.comm₂₃, is_iso.inv_hom_id_assoc,
    S₂.zero, zero_comp], },
  { rw [← cancel_mono (S₁.f), assoc, short_complex.exact.lift_f, f.comm₁₂_assoc,
      is_iso.hom_inv_id, comp_id, id_comp], },
  { rw [← cancel_mono (S₂.f), assoc, f.comm₁₂, short_complex.exact.lift_f_assoc, assoc,
    is_iso.inv_hom_id, comp_id, id_comp], },
end

end short_complex

variables {C D : Type*} [category C] [category D] [abelian C] [abelian D]
  (F : C ⥤ D) [functor.additive F]

namespace injective_embedding

variables [enough_injectives C] (X : C)

def short_complex : short_complex C :=
short_complex.mk (injective.ι X) (cokernel.π (injective.ι X)) (by simp)

instance injective_short_complex_X₂ : injective (short_complex X).X₂ :=
by { dsimp [short_complex], apply_instance, }

instance : mono (short_complex X).f :=
by { dsimp [short_complex], apply_instance, }

instance : epi (short_complex X).g :=
by { dsimp [short_complex], apply_instance, }

lemma short_exact : (short_complex X).short_exact :=
short_complex.short_exact.of_g_is_cokernel (cokernel_is_cokernel _)

end injective_embedding

namespace functor

section

variables {ι : Type*} (c : complex_shape ι) (n : ι)

def single_comp_map_homological_complex_app [decidable_eq ι] (X : C) :
  (F.map_homological_complex c).obj ((homological_complex.single C c n).obj X) ≅
    (homological_complex.single D c n).obj (F.obj X) :=
homological_complex.hom.iso_of_components
(λ i, begin
  by_cases i = n,
  { exact eq_to_iso (by { dsimp, simp only [if_pos h], }), },
  { dsimp,
    simp only [if_neg h],
    exact F.map_zero_object, },
end)
(λ i j hij, begin
  dsimp,
  simp only [F.map_zero, zero_comp, comp_zero],
end)

def single_comp_map_homological_complex [decidable_eq ι] :
  homological_complex.single C c n ⋙ F.map_homological_complex c ≅
    F ⋙ homological_complex.single D c n :=
nat_iso.of_components (F.single_comp_map_homological_complex_app c n)
(λ X Y f, begin
  ext i,
  dsimp [single_comp_map_homological_complex_app],
  by_cases i = n,
  { simp only [dif_pos h, map_comp, eq_to_iso.hom, assoc, eq_to_hom_trans_assoc,
      eq_to_hom_map, eq_to_hom_trans], },
  { simp only [dif_neg h, F.map_zero, zero_comp, comp_zero], },
end)

variable {c}

lemma _root_.homotopy_category_quotient_map_functor_map_homological_complex
  {K L : homological_complex C c} (f : K ⟶ L) (F : C ⥤ D) [F.additive] :
  (homotopy_category.quotient D c).map ((F.map_homological_complex c).map f) =
    (map_homotopy_category c F).map ((homotopy_category.quotient C c).map f) :=
begin
  apply homotopy_category.eq_of_homotopy,
  apply F.map_homotopy,
  apply homotopy_category.homotopy_of_eq,
  simp only [homotopy_category.quotient_map_out],
end

end

instance map_is_strictly_ge (X : cochain_complex C ℤ) (n : ℤ) [X.is_strictly_ge n] :
  cochain_complex.is_strictly_ge ((F.map_homological_complex _ ).obj X) n :=
⟨λ i hi, is_zero.of_iso (is_zero_zero D)
  (F.map_iso (cochain_complex.is_strictly_ge.is_zero X n i hi).iso_zero ≪≫ F.map_zero_object)⟩

lemma _root_.cochain_complex.is_plus.map {X : cochain_complex C ℤ} (h : X.is_plus)
  (F : C ⥤ D) [functor.additive F] :
  cochain_complex.is_plus ((map_homological_complex F (complex_shape.up ℤ)).obj X) :=
begin
  obtain ⟨n, hn⟩ := h,
  haveI := hn,
  exact ⟨n, infer_instance⟩,
end

def map_homotopy_category_factors :
  homotopy_category.quotient _ _ ⋙ map_homotopy_category (complex_shape.up ℤ) F ≅
    F.map_homological_complex _ ⋙ homotopy_category.quotient _ _ :=
nat_iso.of_components (λ K, iso.refl _)
(λ K L f, begin
  dsimp only [iso.refl, functor.comp_map],
  rw [id_comp, comp_id],
  apply homotopy_category.eq_of_homotopy,
  apply F.map_homotopy,
  apply homotopy_category.homotopy_of_eq,
  simp only [homotopy_category.quotient_map_out],
end)

instance map_homotopy_category_has_comm_shift :
  (functor.map_homotopy_category (complex_shape.up ℤ) F).has_comm_shift ℤ :=
@quotient.has_comm_shift _ _ _ _ _ _ _ F.map_homotopy_category_factors ℤ
  _ _ _ (infer_instance : has_shift (homotopy_category C (complex_shape.up ℤ)) ℤ)
  (infer_instance : (homotopy_category.quotient _ _).has_comm_shift ℤ) _

instance : nat_trans.respects_comm_shift F.map_homotopy_category_factors.hom ℤ :=
⟨λ n, begin
  ext K,
  dsimp only [map_homotopy_category_factors, nat_iso.of_components, whisker_right,
    nat_trans.comp_app, iso.refl, whisker_left],
  erw [functor.map_id, comp_id, id_comp],
  apply homotopy_category.eq_of_homotopy,
  erw [id_comp, id_comp, id_comp, id_comp, id_comp, id_comp, id_comp, id_comp,
    comp_id, comp_id, comp_id],
  apply homotopy_category.homotopy_of_eq,
  simp only [functor.map_comp, homotopy_category.quotient_map_out,
    homotopy_category_quotient_map_functor_map_homological_complex, iso.symm_hom,
    ← functor.map_comp_assoc],
  erw [← functor.map_comp, iso.hom_inv_id_app, functor.map_id, id_comp],
end⟩

def map_homotopy_category_plus : homotopy_category.plus C ⥤ homotopy_category.plus D :=
full_subcategory.lift _ (homotopy_category.plus.ι ⋙ functor.map_homotopy_category _ F)
  (λ K, cochain_complex.is_plus.map K.2 F)

def map_homotopy_category_plus_factors :
  F.map_homotopy_category_plus ⋙ homotopy_category.plus.ι ≅
    homotopy_category.plus.ι ⋙ functor.map_homotopy_category _ F :=
full_subcategory.lift_comp_inclusion _ _ _

instance map_homotopy_category_is_triangulated :
  (map_homotopy_category (complex_shape.up ℤ) F).is_triangulated :=
⟨λ T hT, begin
  rw homotopy_category.triangle_distinguished_iff at hT ⊢,
  obtain ⟨K, L, f, ⟨e⟩⟩ := hT,
  exact ⟨_, _, (F.map_homological_complex _).map f,
    ⟨(map_homotopy_category (complex_shape.up ℤ) F).map_triangle.map_iso e ≪≫
    (map_triangle_comp (homotopy_category.quotient C (complex_shape.up ℤ))
    (map_homotopy_category (complex_shape.up ℤ) F)).symm.app _ ≪≫
    (map_triangle_nat_iso F.map_homotopy_category_factors).app _ ≪≫
    (map_triangle_comp (F.map_homological_complex (complex_shape.up ℤ))
    (homotopy_category.quotient D (complex_shape.up ℤ))).app _ ≪≫
    (homotopy_category.quotient D (complex_shape.up ℤ)).map_triangle.map_iso
      (cochain_complex.mapping_cone.triangle_map_iso f F)⟩⟩,
end⟩

instance map_homotopy_category_plus_has_comm_shift :
  (functor.map_homotopy_category_plus F).has_comm_shift ℤ :=
by { dsimp only [map_homotopy_category_plus], apply_instance, }

instance map_homotopy_category_plus_is_triangulated :
  (functor.map_homotopy_category_plus F).is_triangulated :=
by { dsimp only [map_homotopy_category_plus], apply_instance, }

variable [hF : (functor.map_homotopy_category_plus F ⋙
    derived_category.plus.Qh).has_right_derived_functor
    (triangulated.subcategory.W (homotopy_category.plus.acyclic C))]

include hF

abbreviation right_derived_functor_plus : derived_category.plus C ⥤ derived_category.plus D :=
  (functor.map_homotopy_category_plus F ⋙
    derived_category.plus.Qh).right_derived_functor derived_category.plus.Qh
      (triangulated.subcategory.W (homotopy_category.plus.acyclic C))

def right_derived_functor_plus_αh :
  functor.map_homotopy_category_plus F ⋙
    derived_category.plus.Qh ⟶ derived_category.plus.Qh ⋙
      right_derived_functor_plus F :=
functor.right_derived_functor_α _ _ _

def abelian_right_derived_functor (n : ℕ) : C ⥤ D :=
derived_category.plus.single_functor C 0 ⋙ right_derived_functor_plus F ⋙
  derived_category.plus.homology_functor D (n : ℤ)

instance abelian_right_derived_functor_additive (n : ℕ)
  [F.right_derived_functor_plus.is_triangulated] :
  (F.abelian_right_derived_functor n).additive :=
by { dsimp only [abelian_right_derived_functor], apply_instance, }

omit hF

instance single_functor_is_termwise_injective (X : C) (n : ℤ) [injective X] :
  ((homotopy_category.plus.single_functor C n).obj X).obj.as.is_termwise_injective :=
begin
  change ((homological_complex.single C (complex_shape.up ℤ) n).obj X).is_termwise_injective,
  apply_instance,
end

instance (X : homotopy_category.plus C) [X.obj.as.is_termwise_injective]
  [enough_injectives C] :
  is_iso (F.right_derived_functor_plus_αh.app X) :=
by { dsimp only [right_derived_functor_plus_αh], apply_instance, }

def map_homotopy_plus_single_functor_homology_iso_zero :
  F ≅ homotopy_category.plus.single_functor C 0 ⋙ F.map_homotopy_category_plus ⋙
    derived_category.plus.Qh ⋙ derived_category.plus.homology_functor D 0 :=
begin
  change F ≅ homotopy_category.plus.single_functor C 0 ⋙ F.map_homotopy_category_plus ⋙
    derived_category.plus.Qh ⋙ derived_category.plus.ι ⋙ derived_category.homology_functor D 0,
  refine F.right_unitor.symm ≪≫
    iso_whisker_left F (homological_complex.single_homology_functor_iso D (complex_shape.up ℤ) 0).symm ≪≫
    (functor.associator _ _ _).symm ≪≫
    iso_whisker_right (F.single_comp_map_homological_complex (complex_shape.up ℤ) 0).symm
      (homology_functor D (complex_shape.up ℤ) 0) ≪≫
    functor.associator _ _ _ ≪≫ iso_whisker_left _ _ ≪≫ (functor.associator _ _ _).symm ≪≫
    iso_whisker_right (homotopy_category.plus.single_functor_factors C 0).symm _ ≪≫
    functor.associator _ _ _ ≪≫ iso_whisker_left _ (functor.associator _ _ _).symm ≪≫
    iso_whisker_left _ (iso_whisker_right (F.map_homotopy_category_plus_factors).symm _
      ≪≫ functor.associator _ _ _ ≪≫ iso_whisker_left _ (functor.associator _ _ _).symm) ≪≫
    iso_whisker_left _ (iso_whisker_left _
      (iso_whisker_right (derived_category.plus.Qh_comp_ι_iso D).symm
      (derived_category.homology_functor D 0) ≪≫ functor.associator _ _ _)),
  refine iso_whisker_left _ _ ≪≫ (functor.associator _ _ _).symm ≪≫
    iso_whisker_right F.map_homotopy_category_factors.symm _ ≪≫
    functor.associator _ _ _,
  refine (homotopy_category.homology_factors D (complex_shape.up ℤ) 0).symm ≪≫
    iso_whisker_left _ (derived_category.homology_functor_factors_Qh D 0).symm,
end

instance derived_category_plus_single_functor_obj_obj_is_ge (X : C) (n : ℤ) :
  ((derived_category.plus.single_functor C n).obj X).obj.is_ge n :=
begin
  change ((derived_category.single_functor C n).obj X).is_ge n,
  apply_instance,
end

include hF

instance right_derived_functor_plus_obj_is_ge [enough_injectives C]
  (K : derived_category.plus C) (n : ℤ) [K.obj.is_ge n] :
  (F.right_derived_functor_plus.obj K).obj.is_ge n :=
begin
  obtain ⟨K', hK', ⟨e⟩⟩ := derived_category.exists_iso_Q_obj_of_ge K.obj n,
  haveI := hK',
  obtain ⟨Z, hZ, f, hf, hZ'⟩ := homotopy_category.plus.termwise_injective.right_resolution_exists K' n,
  let Z' : homotopy_category.plus C :=
    ⟨(homotopy_category.quotient _ _).obj Z, ⟨n, hZ⟩⟩,
  haveI : Z'.obj.as.is_termwise_injective := hZ',
  let e' : K ≅ derived_category.plus.Qh.obj Z' :=
    derived_category.plus.ι.preimage_iso (e ≪≫ as_iso (derived_category.Q.map f)),
  have e'' := (derived_category.Qh.map_iso ((map_homotopy_category_factors F).app Z)).symm ≪≫
    (derived_category.Qh.map_iso (F.map_homotopy_category_plus_factors.app Z')).symm ≪≫
    (derived_category.plus.Qh_comp_ι_iso D).symm.app
    (F.map_homotopy_category_plus.obj Z') ≪≫ derived_category.plus.ι.map_iso
    (as_iso (F.right_derived_functor_plus_αh.app Z')) ≪≫
    ((F.right_derived_functor_plus ⋙ derived_category.plus.ι).map_iso e'.symm),
  erw ← derived_category.is_ge.iff_of_iso e'' n,
  change (derived_category.Q.obj _).is_ge n,
  apply_instance,
end

def abelian_right_derived_functor_α : F ⟶ F.abelian_right_derived_functor 0 :=
begin
  refine _ ≫ whisker_right (whisker_left (homotopy_category.plus.single_functor C 0)
    F.right_derived_functor_plus_αh) (derived_category.plus.homology_functor D 0) ≫ 𝟙 _,
  { exact F.map_homotopy_plus_single_functor_homology_iso_zero.hom, },
end

lemma abelian_right_derived_functor_α_app (X : C) :
  F.abelian_right_derived_functor_α.app X =
  F.map_homotopy_plus_single_functor_homology_iso_zero.hom.app X ≫
    (derived_category.plus.homology_functor D 0).map
      (F.right_derived_functor_plus_αh.app ((homotopy_category.plus.single_functor C 0).obj X)) :=
begin
  dsimp only [abelian_right_derived_functor_α, whisker_right, whisker_left,
    nat_trans.comp_app, nat_trans.id_app],
  rw comp_id,
end

instance is_iso_abelian_right_derived_functor_plus_α_app (X : C) [injective X] [enough_injectives C] :
  is_iso (F.abelian_right_derived_functor_α.app X) :=
begin
  rw abelian_right_derived_functor_α_app,
  apply_instance,
end

lemma abelian_right_derived_functor_obj_is_zero_of_injective'
  (X : C) [injective X] [enough_injectives C] (n : ℕ) (hn : 1 ≤ n) :
  limits.is_zero ((F.abelian_right_derived_functor n).obj X) :=
begin
  refine is_zero.of_iso _ (((derived_category.plus.homology_functor D n).map_iso
    (as_iso (F.right_derived_functor_plus_αh.app
      ((homotopy_category.plus.single_functor C 0).obj X)))).symm),
  have h : limits.is_zero ((derived_category.homology_functor D n).obj
    ((derived_category.single_functor D 0).obj (F.obj X))),
  { apply derived_category.is_le.is_zero _ 0,
    rw ← int.coe_nat_le_coe_nat_iff at hn,
    rw [algebra_map.coe_one] at hn,
    linarith, },
  refine is_zero.of_iso h ((derived_category.homology_functor D ↑n).map_iso _),
  let e : homotopy_category.plus.single_functor C 0 ⋙ F.map_homotopy_category_plus ⋙
    derived_category.plus.Qh ⋙ derived_category.plus.ι ≅ F ⋙ derived_category.single_functor D 0,
  { refine iso_whisker_left _ (iso_whisker_left _ (derived_category.plus.Qh_comp_ι_iso D)) ≪≫
      iso_whisker_left _ ((functor.associator _ _ _).symm ≪≫
      iso_whisker_right F.map_homotopy_category_plus_factors derived_category.Qh) ≪≫
      iso_whisker_left _ (functor.associator _ _ _) ≪≫
      (functor.associator _ _ _).symm ≪≫
      iso_whisker_right (homotopy_category.plus.single_functor_factors C 0)
        (map_homotopy_category (complex_shape.up ℤ) F ⋙ derived_category.Qh) ≪≫
      functor.associator _ _ _ ≪≫
      iso_whisker_left _ ((functor.associator _ _ _).symm ≪≫
      iso_whisker_right F.map_homotopy_category_factors _) ≪≫
      iso_whisker_left _ (functor.associator _ _ _) ≪≫
      (functor.associator _ _ _).symm ≪≫
      iso_whisker_right (F.single_comp_map_homological_complex (complex_shape.up ℤ) 0) derived_category.Q, },
  exact e.app _,
end

lemma abelian_right_derived_functor_obj_is_zero_of_injective (X : C)
  [injective X] [enough_injectives C] (n : ℕ) :
  limits.is_zero ((F.abelian_right_derived_functor (n+1)).obj X) :=
abelian_right_derived_functor_obj_is_zero_of_injective' _ _ _ (by linarith)

namespace abelian_right_derived_functor_homology_sequence

variables {S : short_complex C} (ex : S.short_exact) (n : ℕ )

def triangle : pretriangulated.triangle (derived_category.plus D) :=
F.right_derived_functor_plus.map_triangle.obj (derived_category.plus.triangle_of_ses
  (short_complex.short_exact.map_of_exact ex (homological_complex.single C (complex_shape.up ℤ) 0))
  (by { dsimp, exact ⟨0, infer_instance⟩, })
  (by { dsimp, exact ⟨0, infer_instance⟩, })
  (by { dsimp, exact ⟨0, infer_instance⟩, }))

def triangle' : pretriangulated.triangle (derived_category D) :=
derived_category.plus.ι.map_triangle.obj (triangle F ex)

variable [hF' : F.right_derived_functor_plus.is_triangulated]

include hF'

lemma triangle_mem : (triangle F ex).distinguished :=
F.right_derived_functor_plus.map_distinguished _
  (derived_category.plus.triangle_of_ses_dist _ _ _ _)

lemma triangle'_mem : (triangle' F ex).distinguished :=
derived_category.plus.ι.map_distinguished _ (triangle_mem F ex)

lemma ex₂ (n : ℕ) :
  (short_complex.mk ((F.abelian_right_derived_functor n).map S.f)
    ((F.abelian_right_derived_functor n).map S.g)
    (by { rw [← functor.map_comp, S.zero, functor.map_zero], })).exact :=
derived_category.homology_sequence.ex₂ (triangle'_mem F ex) n

def δ (n₀ n₁ : ℕ) (h : n₁ = n₀+1) :
  (F.abelian_right_derived_functor n₀).obj S.X₃ ⟶ (F.abelian_right_derived_functor n₁).obj S.X₁ :=
derived_category.homology_sequence.δ (triangle'_mem F ex) n₀ n₁ (by simp [h])

@[simp, reassoc]
lemma δ_comp (n₀ n₁ : ℕ) (h : n₁ = n₀+1) :
  δ F ex n₀ n₁ h ≫ (F.abelian_right_derived_functor n₁).map S.f = 0 :=
derived_category.homology_sequence.δ_comp (triangle'_mem F ex) n₀ n₁ (by simp [h])

@[simp, reassoc]
lemma comp_δ (n₀ n₁ : ℕ) (h : n₁ = n₀+1) :
   (F.abelian_right_derived_functor n₀).map S.g ≫ δ F ex n₀ n₁ h = 0 :=
derived_category.homology_sequence.comp_δ (triangle'_mem F ex) n₀ n₁ (by simp [h])

lemma ex₃ (n₀ n₁ : ℕ) (h : n₁ = n₀+1) :
  (short_complex.mk ((F.abelian_right_derived_functor n₀).map S.g) (δ F ex n₀ n₁ h)
    (by simp)).exact :=
derived_category.homology_sequence.ex₃ (triangle'_mem F ex) n₀ n₁ (by simp [h])

lemma ex₁ (n₀ n₁ : ℕ) (h : n₁ = n₀+1) :
  (short_complex.mk (δ F ex n₀ n₁ h) ((F.abelian_right_derived_functor n₁).map S.f)
    (by simp)).exact :=
derived_category.homology_sequence.ex₁ (triangle'_mem F ex) n₀ n₁ (by simp [h])

include ex

lemma ex₀
  [(F.right_derived_functor_plus.obj
 ((derived_category.plus.single_functor C 0).obj S.X₃)).obj.is_ge 0] :
  mono ((F.abelian_right_derived_functor 0).map S.f) :=
begin
  refine (short_complex.exact_iff_mono _ (is_zero.eq_of_src _ _ _)).1
    (derived_category.homology_sequence.ex₁ (triangle'_mem F ex) (-1) 0 (neg_add_self 1).symm),
  have h := derived_category.is_ge.is_zero ((F.right_derived_functor_plus.obj
   ((derived_category.plus.single_functor C 0).obj S.X₃)).obj) 0 (-1) (by simp),
  exact h,
end

omit ex
omit hF'

instance (X : C) [F.preserves_monomorphisms] [enough_injectives C]:
  mono (F.abelian_right_derived_functor_α.app X) :=
begin
  suffices : mono (F.abelian_right_derived_functor_α.app X ≫
    (F.abelian_right_derived_functor 0).map (injective.ι X)),
  { haveI := this,
    exact mono_of_mono _ ((F.abelian_right_derived_functor 0).map (injective.ι X)), },
  rw ← nat_trans.naturality,
  apply_instance,
end

instance (X : C) [preserves_finite_limits F] [enough_injectives C] :
  is_iso (F.abelian_right_derived_functor_α.app X) :=
begin
  haveI : mono ((injective_embedding.short_complex X).map (F.abelian_right_derived_functor 0)).f :=
    ex₀ F (injective_embedding.short_exact X),
  haveI : mono ((injective_embedding.short_complex X).map F).f,
  { dsimp, apply_instance, },
  let f := short_complex.map_nat_trans (injective_embedding.short_complex X)
    F.abelian_right_derived_functor_α,
  haveI : mono f.τ₃ := (infer_instance : mono (F.abelian_right_derived_functor_α.app _)),
  haveI : is_iso f.τ₂ := (infer_instance : is_iso (F.abelian_right_derived_functor_α.app _)),
  refine short_complex.five_lemma.is_iso_τ₁ f _,
  apply short_complex.exact.of_f_is_kernel,
  let e : parallel_pair (injective_embedding.short_complex X).g 0 ⋙ F ≅
    parallel_pair (F.map (injective_embedding.short_complex X).g) 0 :=
    parallel_pair.ext (iso.refl _) (iso.refl _) (by tidy) (by tidy),
  equiv_rw (limits.is_limit.postcompose_inv_equiv e _).symm,
  refine limits.is_limit.of_iso_limit
    (is_limit_of_preserves F ((injective_embedding.short_exact X).exact.f_is_kernel))
    (cones.ext (iso.refl _) _),
  rintro (_|_),
  { tidy, },
  { dsimp,
    simp only [short_complex.zero, functor.map_zero, comp_id, id_comp,
      ← F.map_comp], },
end

instance [preserves_finite_limits F] [enough_injectives C] :
  is_iso F.abelian_right_derived_functor_α :=
nat_iso.is_iso_of_is_iso_app _

end abelian_right_derived_functor_homology_sequence

end functor

end category_theory