/-
Copyright (c) 2020 Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bhavik Mehta
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.shapes.terminal
import Mathlib.category_theory.limits.shapes.pullbacks
import Mathlib.category_theory.limits.shapes.binary_products
import Mathlib.PostPort

universes v u

namespace Mathlib

/-!
# Constructing binary product from pullbacks and terminal object.

If a category has pullbacks and a terminal object, then it has binary products.

TODO: provide the dual result.
-/

/-- Any category with pullbacks and terminal object has binary products. -/
-- This is not an instance, as it is not always how one wants to construct binary products!

theorem has_binary_products_of_terminal_and_pullbacks (C : Type u) [𝒞 : category_theory.category C] [category_theory.limits.has_terminal C] [category_theory.limits.has_pullbacks C] : category_theory.limits.has_binary_products C := sorry


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

! This file was ported from Lean 3 source module analysis.normed.field.infinite_sum
! leanprover-community/mathlib commit 10bf4f825ad729c5653adc039dafa3622e7f93c9
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Normed.Field.Basic
import Mathbin.Analysis.Normed.Group.InfiniteSum

/-! # Multiplying two infinite sums in a normed ring

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

In this file, we prove various results about `(∑' x : ι, f x) * (∑' y : ι', g y)` in a normed
ring. There are similar results proven in `topology/algebra/infinite_sum` (e.g `tsum_mul_tsum`),
but in a normed ring we get summability results which aren't true in general.

We first establish results about arbitrary index types, `β` and `γ`, and then we specialize to
`β = γ = ℕ` to prove the Cauchy product formula
(see `tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm`).
!-/

variable {α : Type _} {ι : Type _} {ι' : Type _} [NormedRing α]

open BigOperators Classical

open Finset

/-! ### Arbitrary index types -/

/- warning: summable.mul_of_nonneg -> Summable.mul_of_nonneg is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {ι' : Type.{u2}} {f : ι -> Real} {g : ι' -> Real}, (Summable.{0, u1} Real ι Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) f) -> (Summable.{0, u2} Real ι' Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) g) -> (LE.le.{u1} (ι -> Real) (Pi.hasLe.{u1, 0} ι (fun (ᾰ : ι) => Real) (fun (i : ι) => Real.hasLe)) (OfNat.ofNat.{u1} (ι -> Real) 0 (OfNat.mk.{u1} (ι -> Real) 0 (Zero.zero.{u1} (ι -> Real) (Pi.instZero.{u1, 0} ι (fun (ᾰ : ι) => Real) (fun (i : ι) => Real.hasZero))))) f) -> (LE.le.{u2} (ι' -> Real) (Pi.hasLe.{u2, 0} ι' (fun (ᾰ : ι') => Real) (fun (i : ι') => Real.hasLe)) (OfNat.ofNat.{u2} (ι' -> Real) 0 (OfNat.mk.{u2} (ι' -> Real) 0 (Zero.zero.{u2} (ι' -> Real) (Pi.instZero.{u2, 0} ι' (fun (ᾰ : ι') => Real) (fun (i : ι') => Real.hasZero))))) g) -> (Summable.{0, max u1 u2} Real (Prod.{u1, u2} ι ι') Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Prod.{u1, u2} ι ι') => HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.hasMul) (f (Prod.fst.{u1, u2} ι ι' x)) (g (Prod.snd.{u1, u2} ι ι' x))))
but is expected to have type
  forall {ι : Type.{u2}} {ι' : Type.{u1}} {f : ι -> Real} {g : ι' -> Real}, (Summable.{0, u2} Real ι Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) f) -> (Summable.{0, u1} Real ι' Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) g) -> (LE.le.{u2} (ι -> Real) (Pi.hasLe.{u2, 0} ι (fun (ᾰ : ι) => Real) (fun (i : ι) => Real.instLEReal)) (OfNat.ofNat.{u2} (ι -> Real) 0 (Zero.toOfNat0.{u2} (ι -> Real) (Pi.instZero.{u2, 0} ι (fun (a._@.Mathlib.Analysis.Normed.Field.InfiniteSum._hyg.27 : ι) => Real) (fun (i : ι) => Real.instZeroReal)))) f) -> (LE.le.{u1} (ι' -> Real) (Pi.hasLe.{u1, 0} ι' (fun (ᾰ : ι') => Real) (fun (i : ι') => Real.instLEReal)) (OfNat.ofNat.{u1} (ι' -> Real) 0 (Zero.toOfNat0.{u1} (ι' -> Real) (Pi.instZero.{u1, 0} ι' (fun (a._@.Mathlib.Analysis.Normed.Field.InfiniteSum._hyg.32 : ι') => Real) (fun (i : ι') => Real.instZeroReal)))) g) -> (Summable.{0, max u2 u1} Real (Prod.{u2, u1} ι ι') Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Prod.{u2, u1} ι ι') => HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.instMulReal) (f (Prod.fst.{u2, u1} ι ι' x)) (g (Prod.snd.{u2, u1} ι ι' x))))
Case conversion may be inaccurate. Consider using '#align summable.mul_of_nonneg Summable.mul_of_nonnegₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem Summable.mul_of_nonneg {f : ι → ℝ} {g : ι' → ℝ} (hf : Summable f) (hg : Summable g)
    (hf' : 0 ≤ f) (hg' : 0 ≤ g) : Summable fun x : ι × ι' => f x.1 * g x.2 :=
  let ⟨s, hf⟩ := hf
  let ⟨t, hg⟩ := hg
  suffices this : ∀ u : Finset (ι × ι'), (∑ x in u, f x.1 * g x.2) ≤ s * t from
    summable_of_sum_le (fun x => mul_nonneg (hf' _) (hg' _)) this
  fun u =>
  calc
    (∑ x in u, f x.1 * g x.2) ≤ ∑ x in u.image Prod.fst ×ˢ u.image Prod.snd, f x.1 * g x.2 :=
      sum_mono_set_of_nonneg (fun x => mul_nonneg (hf' _) (hg' _)) subset_product
    _ = ∑ x in u.image Prod.fst, ∑ y in u.image Prod.snd, f x * g y := sum_product
    _ = ∑ x in u.image Prod.fst, f x * ∑ y in u.image Prod.snd, g y :=
      (sum_congr rfl fun x _ => mul_sum.symm)
    _ ≤ ∑ x in u.image Prod.fst, f x * t :=
      (sum_le_sum fun x _ =>
        mul_le_mul_of_nonneg_left (sum_le_hasSum _ (fun _ _ => hg' _) hg) (hf' _))
    _ = (∑ x in u.image Prod.fst, f x) * t := sum_mul.symm
    _ ≤ s * t :=
      mul_le_mul_of_nonneg_right (sum_le_hasSum _ (fun _ _ => hf' _) hf) (hg.NonNeg fun _ => hg' _)

#align summable.mul_of_nonneg Summable.mul_of_nonneg

/- warning: summable.mul_norm -> Summable.mul_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {ι : Type.{u2}} {ι' : Type.{u3}} [_inst_1 : NormedRing.{u1} α] {f : ι -> α} {g : ι' -> α}, (Summable.{0, u2} Real ι Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (f x))) -> (Summable.{0, u3} Real ι' Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι') => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (g x))) -> (Summable.{0, max u2 u3} Real (Prod.{u2, u3} ι ι') Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Prod.{u2, u3} ι ι') => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (NormedRing.toRing.{u1} α _inst_1)))) (f (Prod.fst.{u2, u3} ι ι' x)) (g (Prod.snd.{u2, u3} ι ι' x)))))
but is expected to have type
  forall {α : Type.{u2}} {ι : Type.{u3}} {ι' : Type.{u1}} [_inst_1 : NormedRing.{u2} α] {f : ι -> α} {g : ι' -> α}, (Summable.{0, u3} Real ι Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι) => Norm.norm.{u2} α (NormedRing.toNorm.{u2} α _inst_1) (f x))) -> (Summable.{0, u1} Real ι' Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι') => Norm.norm.{u2} α (NormedRing.toNorm.{u2} α _inst_1) (g x))) -> (Summable.{0, max u3 u1} Real (Prod.{u3, u1} ι ι') Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Prod.{u3, u1} ι ι') => Norm.norm.{u2} α (NormedRing.toNorm.{u2} α _inst_1) (HMul.hMul.{u2, u2, u2} α α α (instHMul.{u2} α (NonUnitalNonAssocRing.toMul.{u2} α (NonAssocRing.toNonUnitalNonAssocRing.{u2} α (Ring.toNonAssocRing.{u2} α (NormedRing.toRing.{u2} α _inst_1))))) (f (Prod.fst.{u3, u1} ι ι' x)) (g (Prod.snd.{u3, u1} ι ι' x)))))
Case conversion may be inaccurate. Consider using '#align summable.mul_norm Summable.mul_normₓ'. -/
theorem Summable.mul_norm {f : ι → α} {g : ι' → α} (hf : Summable fun x => ‖f x‖)
    (hg : Summable fun x => ‖g x‖) : Summable fun x : ι × ι' => ‖f x.1 * g x.2‖ :=
  summable_of_nonneg_of_le (fun x => norm_nonneg (f x.1 * g x.2))
    (fun x => norm_mul_le (f x.1) (g x.2))
    (hf.mul_of_nonneg hg (fun x => norm_nonneg <| f x) fun x => norm_nonneg <| g x : _)
#align summable.mul_norm Summable.mul_norm

/- warning: summable_mul_of_summable_norm -> summable_mul_of_summable_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {ι : Type.{u2}} {ι' : Type.{u3}} [_inst_1 : NormedRing.{u1} α] [_inst_2 : CompleteSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))] {f : ι -> α} {g : ι' -> α}, (Summable.{0, u2} Real ι Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (f x))) -> (Summable.{0, u3} Real ι' Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι') => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (g x))) -> (Summable.{u1, max u2 u3} α (Prod.{u2, u3} ι ι') (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) (fun (x : Prod.{u2, u3} ι ι') => HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (NormedRing.toRing.{u1} α _inst_1)))) (f (Prod.fst.{u2, u3} ι ι' x)) (g (Prod.snd.{u2, u3} ι ι' x))))
but is expected to have type
  forall {α : Type.{u3}} {ι : Type.{u2}} {ι' : Type.{u1}} [_inst_1 : NormedRing.{u3} α] [_inst_2 : CompleteSpace.{u3} α (PseudoMetricSpace.toUniformSpace.{u3} α (SeminormedRing.toPseudoMetricSpace.{u3} α (NormedRing.toSeminormedRing.{u3} α _inst_1)))] {f : ι -> α} {g : ι' -> α}, (Summable.{0, u2} Real ι Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι) => Norm.norm.{u3} α (NormedRing.toNorm.{u3} α _inst_1) (f x))) -> (Summable.{0, u1} Real ι' Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι') => Norm.norm.{u3} α (NormedRing.toNorm.{u3} α _inst_1) (g x))) -> (Summable.{u3, max u2 u1} α (Prod.{u2, u1} ι ι') (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u3} α (NonAssocRing.toNonUnitalNonAssocRing.{u3} α (Ring.toNonAssocRing.{u3} α (NormedRing.toRing.{u3} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u3} α (PseudoMetricSpace.toUniformSpace.{u3} α (SeminormedRing.toPseudoMetricSpace.{u3} α (NormedRing.toSeminormedRing.{u3} α _inst_1)))) (fun (x : Prod.{u2, u1} ι ι') => HMul.hMul.{u3, u3, u3} α α α (instHMul.{u3} α (NonUnitalNonAssocRing.toMul.{u3} α (NonAssocRing.toNonUnitalNonAssocRing.{u3} α (Ring.toNonAssocRing.{u3} α (NormedRing.toRing.{u3} α _inst_1))))) (f (Prod.fst.{u2, u1} ι ι' x)) (g (Prod.snd.{u2, u1} ι ι' x))))
Case conversion may be inaccurate. Consider using '#align summable_mul_of_summable_norm summable_mul_of_summable_normₓ'. -/
theorem summable_mul_of_summable_norm [CompleteSpace α] {f : ι → α} {g : ι' → α}
    (hf : Summable fun x => ‖f x‖) (hg : Summable fun x => ‖g x‖) :
    Summable fun x : ι × ι' => f x.1 * g x.2 :=
  summable_of_summable_norm (hf.mul_norm hg)
#align summable_mul_of_summable_norm summable_mul_of_summable_norm

/- warning: tsum_mul_tsum_of_summable_norm -> tsum_mul_tsum_of_summable_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {ι : Type.{u2}} {ι' : Type.{u3}} [_inst_1 : NormedRing.{u1} α] [_inst_2 : CompleteSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))] {f : ι -> α} {g : ι' -> α}, (Summable.{0, u2} Real ι Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (f x))) -> (Summable.{0, u3} Real ι' Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι') => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (g x))) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (NormedRing.toRing.{u1} α _inst_1)))) (tsum.{u1, u2} α (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) ι (fun (x : ι) => f x)) (tsum.{u1, u3} α (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) ι' (fun (y : ι') => g y))) (tsum.{u1, max u2 u3} α (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) (Prod.{u2, u3} ι ι') (fun (z : Prod.{u2, u3} ι ι') => HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (NormedRing.toRing.{u1} α _inst_1)))) (f (Prod.fst.{u2, u3} ι ι' z)) (g (Prod.snd.{u2, u3} ι ι' z)))))
but is expected to have type
  forall {α : Type.{u3}} {ι : Type.{u2}} {ι' : Type.{u1}} [_inst_1 : NormedRing.{u3} α] [_inst_2 : CompleteSpace.{u3} α (PseudoMetricSpace.toUniformSpace.{u3} α (SeminormedRing.toPseudoMetricSpace.{u3} α (NormedRing.toSeminormedRing.{u3} α _inst_1)))] {f : ι -> α} {g : ι' -> α}, (Summable.{0, u2} Real ι Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι) => Norm.norm.{u3} α (NormedRing.toNorm.{u3} α _inst_1) (f x))) -> (Summable.{0, u1} Real ι' Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : ι') => Norm.norm.{u3} α (NormedRing.toNorm.{u3} α _inst_1) (g x))) -> (Eq.{succ u3} α (HMul.hMul.{u3, u3, u3} α α α (instHMul.{u3} α (NonUnitalNonAssocRing.toMul.{u3} α (NonAssocRing.toNonUnitalNonAssocRing.{u3} α (Ring.toNonAssocRing.{u3} α (NormedRing.toRing.{u3} α _inst_1))))) (tsum.{u3, u2} α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u3} α (NonAssocRing.toNonUnitalNonAssocRing.{u3} α (Ring.toNonAssocRing.{u3} α (NormedRing.toRing.{u3} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u3} α (PseudoMetricSpace.toUniformSpace.{u3} α (SeminormedRing.toPseudoMetricSpace.{u3} α (NormedRing.toSeminormedRing.{u3} α _inst_1)))) ι (fun (x : ι) => f x)) (tsum.{u3, u1} α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u3} α (NonAssocRing.toNonUnitalNonAssocRing.{u3} α (Ring.toNonAssocRing.{u3} α (NormedRing.toRing.{u3} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u3} α (PseudoMetricSpace.toUniformSpace.{u3} α (SeminormedRing.toPseudoMetricSpace.{u3} α (NormedRing.toSeminormedRing.{u3} α _inst_1)))) ι' (fun (y : ι') => g y))) (tsum.{u3, max u2 u1} α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u3} α (NonAssocRing.toNonUnitalNonAssocRing.{u3} α (Ring.toNonAssocRing.{u3} α (NormedRing.toRing.{u3} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u3} α (PseudoMetricSpace.toUniformSpace.{u3} α (SeminormedRing.toPseudoMetricSpace.{u3} α (NormedRing.toSeminormedRing.{u3} α _inst_1)))) (Prod.{u2, u1} ι ι') (fun (z : Prod.{u2, u1} ι ι') => HMul.hMul.{u3, u3, u3} α α α (instHMul.{u3} α (NonUnitalNonAssocRing.toMul.{u3} α (NonAssocRing.toNonUnitalNonAssocRing.{u3} α (Ring.toNonAssocRing.{u3} α (NormedRing.toRing.{u3} α _inst_1))))) (f (Prod.fst.{u2, u1} ι ι' z)) (g (Prod.snd.{u2, u1} ι ι' z)))))
Case conversion may be inaccurate. Consider using '#align tsum_mul_tsum_of_summable_norm tsum_mul_tsum_of_summable_normₓ'. -/
/-- Product of two infinites sums indexed by arbitrary types.
    See also `tsum_mul_tsum` if `f` and `g` are *not* absolutely summable. -/
theorem tsum_mul_tsum_of_summable_norm [CompleteSpace α] {f : ι → α} {g : ι' → α}
    (hf : Summable fun x => ‖f x‖) (hg : Summable fun x => ‖g x‖) :
    ((∑' x, f x) * ∑' y, g y) = ∑' z : ι × ι', f z.1 * g z.2 :=
  tsum_mul_tsum (summable_of_summable_norm hf) (summable_of_summable_norm hg)
    (summable_mul_of_summable_norm hf hg)
#align tsum_mul_tsum_of_summable_norm tsum_mul_tsum_of_summable_norm

/-! ### `ℕ`-indexed families (Cauchy product)

We prove two versions of the Cauchy product formula. The first one is
`tsum_mul_tsum_eq_tsum_sum_range_of_summable_norm`, where the `n`-th term is a sum over
`finset.range (n+1)` involving `nat` subtraction.
In order to avoid `nat` subtraction, we also provide
`tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm`,
where the `n`-th term is a sum over all pairs `(k, l)` such that `k+l=n`, which corresponds to the
`finset` `finset.nat.antidiagonal n`. -/

section Nat

open Finset.Nat

#print summable_norm_sum_mul_antidiagonal_of_summable_norm /-
theorem summable_norm_sum_mul_antidiagonal_of_summable_norm {f g : ℕ → α}
    (hf : Summable fun x => ‖f x‖) (hg : Summable fun x => ‖g x‖) :
    Summable fun n => ‖∑ kl in antidiagonal n, f kl.1 * g kl.2‖ :=
  by
  have :=
    summable_sum_mul_antidiagonal_of_summable_mul
      (Summable.mul_of_nonneg hf hg (fun _ => norm_nonneg _) fun _ => norm_nonneg _)
  refine' summable_of_nonneg_of_le (fun _ => norm_nonneg _) _ this
  intro n
  calc
    ‖∑ kl in antidiagonal n, f kl.1 * g kl.2‖ ≤ ∑ kl in antidiagonal n, ‖f kl.1 * g kl.2‖ :=
      norm_sum_le _ _
    _ ≤ ∑ kl in antidiagonal n, ‖f kl.1‖ * ‖g kl.2‖ := sum_le_sum fun i _ => norm_mul_le _ _

#align summable_norm_sum_mul_antidiagonal_of_summable_norm summable_norm_sum_mul_antidiagonal_of_summable_norm
-/

/- warning: tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm -> tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedRing.{u1} α] [_inst_2 : CompleteSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))] {f : Nat -> α} {g : Nat -> α}, (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (f x))) -> (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (g x))) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (NormedRing.toRing.{u1} α _inst_1)))) (tsum.{u1, 0} α (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => f n)) (tsum.{u1, 0} α (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => g n))) (tsum.{u1, 0} α (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => Finset.sum.{u1, 0} α (Prod.{0, 0} Nat Nat) (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (Finset.Nat.antidiagonal n) (fun (kl : Prod.{0, 0} Nat Nat) => HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (NormedRing.toRing.{u1} α _inst_1)))) (f (Prod.fst.{0, 0} Nat Nat kl)) (g (Prod.snd.{0, 0} Nat Nat kl))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedRing.{u1} α] [_inst_2 : CompleteSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))] {f : Nat -> α} {g : Nat -> α}, (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toNorm.{u1} α _inst_1) (f x))) -> (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toNorm.{u1} α _inst_1) (g x))) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocRing.toMul.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (tsum.{u1, 0} α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => f n)) (tsum.{u1, 0} α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => g n))) (tsum.{u1, 0} α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => Finset.sum.{u1, 0} α (Prod.{0, 0} Nat Nat) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (Finset.Nat.antidiagonal n) (fun (kl : Prod.{0, 0} Nat Nat) => HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocRing.toMul.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (f (Prod.fst.{0, 0} Nat Nat kl)) (g (Prod.snd.{0, 0} Nat Nat kl))))))
Case conversion may be inaccurate. Consider using '#align tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_normₓ'. -/
/-- The Cauchy product formula for the product of two infinite sums indexed by `ℕ`,
    expressed by summing on `finset.nat.antidiagonal`.
    See also `tsum_mul_tsum_eq_tsum_sum_antidiagonal` if `f` and `g` are
    *not* absolutely summable. -/
theorem tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm [CompleteSpace α] {f g : ℕ → α}
    (hf : Summable fun x => ‖f x‖) (hg : Summable fun x => ‖g x‖) :
    ((∑' n, f n) * ∑' n, g n) = ∑' n, ∑ kl in antidiagonal n, f kl.1 * g kl.2 :=
  tsum_mul_tsum_eq_tsum_sum_antidiagonal (summable_of_summable_norm hf)
    (summable_of_summable_norm hg) (summable_mul_of_summable_norm hf hg)
#align tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm

/- warning: summable_norm_sum_mul_range_of_summable_norm -> summable_norm_sum_mul_range_of_summable_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedRing.{u1} α] {f : Nat -> α} {g : Nat -> α}, (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (f x))) -> (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (g x))) -> (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (n : Nat) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (Finset.sum.{u1, 0} α Nat (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (Finset.range (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (fun (k : Nat) => HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (NormedRing.toRing.{u1} α _inst_1)))) (f k) (g (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) n k))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedRing.{u1} α] {f : Nat -> α} {g : Nat -> α}, (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toNorm.{u1} α _inst_1) (f x))) -> (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toNorm.{u1} α _inst_1) (g x))) -> (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (n : Nat) => Norm.norm.{u1} α (NormedRing.toNorm.{u1} α _inst_1) (Finset.sum.{u1, 0} α Nat (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (Finset.range (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (k : Nat) => HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocRing.toMul.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (f k) (g (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) n k))))))
Case conversion may be inaccurate. Consider using '#align summable_norm_sum_mul_range_of_summable_norm summable_norm_sum_mul_range_of_summable_normₓ'. -/
theorem summable_norm_sum_mul_range_of_summable_norm {f g : ℕ → α} (hf : Summable fun x => ‖f x‖)
    (hg : Summable fun x => ‖g x‖) : Summable fun n => ‖∑ k in range (n + 1), f k * g (n - k)‖ :=
  by
  simp_rw [← sum_antidiagonal_eq_sum_range_succ fun k l => f k * g l]
  exact summable_norm_sum_mul_antidiagonal_of_summable_norm hf hg
#align summable_norm_sum_mul_range_of_summable_norm summable_norm_sum_mul_range_of_summable_norm

/- warning: tsum_mul_tsum_eq_tsum_sum_range_of_summable_norm -> tsum_mul_tsum_eq_tsum_sum_range_of_summable_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedRing.{u1} α] [_inst_2 : CompleteSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))] {f : Nat -> α} {g : Nat -> α}, (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (f x))) -> (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toHasNorm.{u1} α _inst_1) (g x))) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (NormedRing.toRing.{u1} α _inst_1)))) (tsum.{u1, 0} α (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => f n)) (tsum.{u1, 0} α (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => g n))) (tsum.{u1, 0} α (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => Finset.sum.{u1, 0} α Nat (AddCommGroup.toAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NonUnitalNormedRing.toNormedAddCommGroup.{u1} α (NormedRing.toNonUnitalNormedRing.{u1} α _inst_1)))) (Finset.range (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (fun (k : Nat) => HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (Ring.toDistrib.{u1} α (NormedRing.toRing.{u1} α _inst_1)))) (f k) (g (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) n k))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedRing.{u1} α] [_inst_2 : CompleteSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))] {f : Nat -> α} {g : Nat -> α}, (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toNorm.{u1} α _inst_1) (f x))) -> (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (x : Nat) => Norm.norm.{u1} α (NormedRing.toNorm.{u1} α _inst_1) (g x))) -> (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocRing.toMul.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (tsum.{u1, 0} α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => f n)) (tsum.{u1, 0} α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => g n))) (tsum.{u1, 0} α (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedRing.toPseudoMetricSpace.{u1} α (NormedRing.toSeminormedRing.{u1} α _inst_1)))) Nat (fun (n : Nat) => Finset.sum.{u1, 0} α Nat (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} α (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (Finset.range (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (k : Nat) => HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocRing.toMul.{u1} α (NonAssocRing.toNonUnitalNonAssocRing.{u1} α (Ring.toNonAssocRing.{u1} α (NormedRing.toRing.{u1} α _inst_1))))) (f k) (g (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) n k))))))
Case conversion may be inaccurate. Consider using '#align tsum_mul_tsum_eq_tsum_sum_range_of_summable_norm tsum_mul_tsum_eq_tsum_sum_range_of_summable_normₓ'. -/
/-- The Cauchy product formula for the product of two infinite sums indexed by `ℕ`,
    expressed by summing on `finset.range`.
    See also `tsum_mul_tsum_eq_tsum_sum_range` if `f` and `g` are
    *not* absolutely summable. -/
theorem tsum_mul_tsum_eq_tsum_sum_range_of_summable_norm [CompleteSpace α] {f g : ℕ → α}
    (hf : Summable fun x => ‖f x‖) (hg : Summable fun x => ‖g x‖) :
    ((∑' n, f n) * ∑' n, g n) = ∑' n, ∑ k in range (n + 1), f k * g (n - k) :=
  by
  simp_rw [← sum_antidiagonal_eq_sum_range_succ fun k l => f k * g l]
  exact tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm hf hg
#align tsum_mul_tsum_eq_tsum_sum_range_of_summable_norm tsum_mul_tsum_eq_tsum_sum_range_of_summable_norm

end Nat


/-
Copyright (c) 2020 Zhouhang Zhou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.indicator_function
import Mathlib.analysis.normed_space.basic
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

/-!
# Indicator function and norm

This file contains a few simple lemmas about `set.indicator` and `norm`.

## Tags
indicator, norm
-/

theorem norm_indicator_eq_indicator_norm {α : Type u_1} {E : Type u_2} [normed_group E] {s : set α} (f : α → E) (a : α) : norm (set.indicator s f a) = set.indicator s (fun (a : α) => norm (f a)) a :=
  flip congr_fun a (Eq.symm (set.indicator_comp_of_zero norm_zero))

theorem nnnorm_indicator_eq_indicator_nnnorm {α : Type u_1} {E : Type u_2} [normed_group E] {s : set α} (f : α → E) (a : α) : nnnorm (set.indicator s f a) = set.indicator s (fun (a : α) => nnnorm (f a)) a :=
  flip congr_fun a (Eq.symm (set.indicator_comp_of_zero nnnorm_zero))

theorem norm_indicator_le_of_subset {α : Type u_1} {E : Type u_2} [normed_group E] {s : set α} {t : set α} (h : s ⊆ t) (f : α → E) (a : α) : norm (set.indicator s f a) ≤ norm (set.indicator t f a) := sorry

theorem indicator_norm_le_norm_self {α : Type u_1} {E : Type u_2} [normed_group E] {s : set α} (f : α → E) (a : α) : set.indicator s (fun (a : α) => norm (f a)) a ≤ norm (f a) :=
  set.indicator_le_self' (fun (_x : α) (_x : ¬_x ∈ s) => norm_nonneg (f a)) a

theorem norm_indicator_le_norm_self {α : Type u_1} {E : Type u_2} [normed_group E] {s : set α} (f : α → E) (a : α) : norm (set.indicator s f a) ≤ norm (f a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (norm (set.indicator s f a) ≤ norm (f a))) (norm_indicator_eq_indicator_norm f a)))
    (indicator_norm_le_norm_self (fun (a : α) => f a) a)


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

import computability.NFA

/-!
# Epsilon Nondeterministic Finite Automata
This file contains the definition of an epsilon Nondeterministic Finite Automaton (`ε_NFA`), a state
machine which determines whether a string (implemented as a list over an arbitrary alphabet) is in a
regular set by evaluating the string over every possible path, also having access to ε-transitons,
which can be followed without reading a character.
Since this definition allows for automata with infinite states, a `fintype` instance must be
supplied for true `ε_NFA`'s.
-/

universes u v

/-- An `ε_NFA` is a set of states (`σ`), a transition function from state to state labelled by the
  alphabet (`step`), a starting state (`start`) and a set of acceptance states (`accept`).
  Note the transition function sends a state to a `set` of states and can make ε-transitions by
  inputing `none`.
  Since this definition allows for Automata with infinite states, a `fintype` instance must be
  supplied for true `ε_NFA`'s.-/
structure ε_NFA (α : Type u) (σ : Type v) :=
(step : σ → option α → set σ)
(start : set σ)
(accept : set σ)

variables {α : Type u} {σ σ' : Type v} (M : ε_NFA α σ)

namespace ε_NFA

instance : inhabited (ε_NFA α σ) := ⟨ ε_NFA.mk (λ _ _, ∅) ∅ ∅ ⟩

/-- The `ε_closure` of a set is the set of states which can be reached by taking a finite string of
  ε-transitions from an element of the the set -/
inductive ε_closure : set σ → set σ
| base : ∀ S (s ∈ S), ε_closure S s
| step : ∀ S s (t ∈ M.step s none), ε_closure S s → ε_closure S t

/-- `M.step_set S a` is the union of the ε-closure of `M.step s a` for all `s ∈ S`. -/
def step_set : set σ → α → set σ :=
λ S a, S >>= (λ s, M.ε_closure (M.step s a))

/-- `M.eval_from S x` computes all possible paths though `M` with input `x` starting at an element
  of `S`. -/
def eval_from (start : set σ) : list α → set σ :=
list.foldl M.step_set (M.ε_closure start)

/-- `M.eval x` computes all possible paths though `M` with input `x` starting at an element of
  `M.start`. -/
def eval := M.eval_from M.start

/-- `M.accepts` is the language of `x` such that there is an accept state in `M.eval x`. -/
def accepts : language α :=
λ x, ∃ S ∈ M.accept, S ∈ M.eval x

/-- `M.to_NFA` is an `NFA` constructed from an `ε_NFA` `M`. -/
def to_NFA : NFA α σ :=
{ step := λ S a, M.ε_closure (M.step S a),
  start := M.ε_closure M.start,
  accept := M.accept }

@[simp] lemma to_NFA_eval_from_match (start : set σ) :
  M.to_NFA.eval_from (M.ε_closure start) = M.eval_from start := rfl

@[simp] lemma to_NFA_correct :
  M.to_NFA.accepts = M.accepts :=
begin
  ext x,
  rw [accepts, NFA.accepts, eval, NFA.eval, ←to_NFA_eval_from_match],
  refl
end

lemma pumping_lemma [fintype σ] {x : list α} (hx : x ∈ M.accepts)
  (hlen : fintype.card (set σ) + 1 ≤ list.length x) :
  ∃ a b c, x = a ++ b ++ c ∧ a.length + b.length ≤ fintype.card (set σ) + 1 ∧ b ≠ [] ∧
  {a} * language.star {b} * {c} ≤ M.accepts :=
begin
  rw ←to_NFA_correct at hx ⊢,
  exact M.to_NFA.pumping_lemma hx hlen
end

end ε_NFA

namespace NFA

/-- `M.to_ε_NFA` is an `ε_NFA` constructed from an `NFA` `M` by using the same start and accept
  states and transition functions. -/
def to_ε_NFA (M : NFA α σ) : ε_NFA α σ :=
{ step := λ s a, a.cases_on' ∅ (λ a, M.step s a),
  start := M.start,
  accept := M.accept }

@[simp] lemma to_ε_NFA_ε_closure (M : NFA α σ) (S : set σ) : M.to_ε_NFA.ε_closure S = S :=
begin
  ext a,
  split,
  { rintro ( ⟨ _, _, h ⟩ | ⟨ _, _, _, h, _ ⟩ ),
    exact h,
    cases h },
  { intro h,
    apply ε_NFA.ε_closure.base,
    exact h }
end

@[simp] lemma to_ε_NFA_eval_from_match (M : NFA α σ) (start : set σ) :
  M.to_ε_NFA.eval_from start = M.eval_from start :=
begin
  rw [eval_from, ε_NFA.eval_from, step_set, ε_NFA.step_set, to_ε_NFA_ε_closure],
  congr,
  ext S s,
  simp only [exists_prop, set.mem_Union, set.bind_def],
  apply exists_congr,
  simp only [and.congr_right_iff],
  intros t ht,
  rw M.to_ε_NFA_ε_closure,
  refl
end

@[simp] lemma to_ε_NFA_correct (M : NFA α σ) :
  M.to_ε_NFA.accepts = M.accepts :=
begin
  rw [accepts, ε_NFA.accepts, eval, ε_NFA.eval, to_ε_NFA_eval_from_match],
  refl
end

end NFA


-- SPL.lean
-- Software Product Line variability
--import data.fintype
import data.finset

namespace SPL

-- Features and Configurations
section
parameters (Feature : Type) [fintype Feature] [decidable_eq Feature]

@[reducible]
def Config : Type := finset Feature

end -- Features and Configurations section

-- Presence Conditions (PCs)
section
parameters (Feature : Type) [fintype Feature] [decidable_eq Feature]

inductive PC : Type
| All  : PC
| None : PC
| Atom : Feature → PC
| Not  : PC → PC
| And  : PC → PC → PC
| Or   : PC → PC → PC

end

section
parameters {Feature : Type} [fintype Feature] [decidable_eq Feature]

@[reducible]
def ConfigSpace : Type := finset (Config Feature)

@[reducible]
def allConfigs : ConfigSpace := finset.univ

open PC list

def semantics : PC Feature → ConfigSpace
| (All)         := allConfigs
| (None)        := ∅
| (Atom f)      := allConfigs.filter (λ p, f ∈ p)
| (Not pc)      := allConfigs.filter (λ p, p ∈ (semantics pc))
| (And pc₁ pc₂) := (semantics pc₁) ∩ (semantics pc₂)
| (Or  pc₁ pc₂) := (semantics pc₁) ∪ (semantics pc₂)

end -- section PCs

notation `⟦` p `⟧` := (semantics p)

-- Var and Lifted
section
parameters {Feature : Type} [fintype Feature] [decidable_eq Feature]

structure Var (α : Type) := (v : α) (pc : PC Feature)

open list

def disjointPCs {α : Type} (s : list (Var α)) : Prop :=
    ∀ (c : Config Feature) (x₁ x₂ : Var α),
        x₁ ∈ s → x₂ ∈ s →
        c ∈ ⟦x₁.pc⟧ → c ∈ ⟦x₂.pc⟧ → x₁ = x₂

def completePCs {α : Type} (s : list (Var α)) : Prop :=
    ∀ (c : Config Feature), ∃ (v : (Var α)), v ∈ s ∧ c ∈ ⟦v.pc⟧

structure Lifted (α : Type) :=
    (s    : list (Var α))
    (disj : disjointPCs s)
    (comp : completePCs s)

end -- section Var and Lifted

postfix `↑`:(max+1) := Lifted

-- list_find -- helper for index
section

open classical

lemma list_find {α : Type} (q : α → Prop) [decidable_pred q] :
    ∀ (l : list α), (∃ y, y ∈ l ∧ q y) → (list.find q l).is_some :=
begin
    intros l h₁,
    induction l,
    -- base case
    apply exists.elim h₁, intros a h₂, apply and.elim h₂, intros h₃ h₄, simp,
    apply list.not_mem_nil a h₃,
    -- induction
    unfold list.find, cases decidable.em (q l_hd) with hpos hneg,
    { rw if_pos hpos, simp },
    { rw if_neg hneg, apply exists.elim h₁, intros a h₂, apply l_ih, apply exists.intro a,
      simp at h₂, rw and.comm at h₂, rw and_or_distrib_left at h₂, apply or.elim h₂,
      {intro h₃, apply and.elim h₃, intros h₄ h₅, rw← h₅ at hneg, contradiction},
      {intro h₃, rw and.comm, exact h₃}
    }
end

end --section list_find

-- indexing
section
variables {Feature: Type} [fintype Feature] [decidable_eq Feature] {α : Type}

def index'' (v : Lifted α) (ρ : Config Feature) : option (Var α) :=
    list.find (λ (u: @Var Feature _inst_1 _inst_2 α), ρ ∈ ⟦u.pc⟧) v.s

def configRel (v : Lifted α) (c₁ c₂ : Config Feature) : Prop :=
    index'' v c₁ = index'' v c₂

lemma configRelReflexive (v : α↑) : ∀ (ρ : Config Feature), configRel v ρ ρ :=
begin intros ρ, unfold configRel end

lemma configRelSymmetric (v: α↑) :
    ∀ (c₁ c₂ : Config Feature), configRel v c₁ c₂ → configRel v c₂ c₁ :=
begin
    intros c₁ c₂, unfold configRel, intros h, rw h
end

lemma configRelTransitive (v: α↑) :
    ∀ (c₁ c₂ c₃: Config Feature),
        configRel v c₁ c₂ → configRel v c₂ c₃ → configRel v c₁ c₃ :=
begin
    intros c₁ c₂ c₃, unfold configRel, intros h₁ h₂, rw h₁, rw← h₂
end

theorem configRel_equiv (α : Type) (v : Lifted α) : @equivalence (Config Feature) (configRel v) :=
mk_equivalence (configRel v) (configRelReflexive v) (configRelSymmetric v) (configRelTransitive v)

instance lifted_setoid (α : Type) (v : α↑): setoid (Config Feature) :=
setoid.mk (configRel v) (configRel_equiv α v)

#print lifted_setoid

def index' --[t : fintype Feature] [d : decidable_eq Feature]
    (v : Lifted α) (ρ : Config Feature) : Var α :=
    let pred := λ (u: @Var Feature _inst_1 _inst_2 α), ρ ∈ semantics (u.pc),
        r    := list.find pred v.s in
    if  h : r.is_some
    then option.get h
    else false.elim
        begin
            apply h, apply (list_find pred v.s (v.comp ρ))
        end

def index (v : Lifted α) (ρ : Config Feature) : α :=
    (index' v ρ).v

lemma index_unique (v : Lifted α) (x : Var α) (ρ : Config Feature)
    : x ∈ v.s → ρ ∈ semantics (x.pc) → (index'' v ρ) = some x :=
begin
    intros h1 h2,
    unfold index'',
    --generalize_hyp h3 : (hd :: tl) = xs,
    induction h3 : v.s generalizing x,
    -- base case
    rw h3 at h1, simp at h1, contradiction,
    -- induction
    unfold list.find, split_ifs,
    apply v.disj, rw h3, simp,
    exact h1,
    exact h,
    exact h2,
    apply ih,

    --simp, split_ifs,
    {rw option.get_of_mem, simp, }
end

end -- indexing section

end SPL


-- Copyright (C) 2020 by @ljt12138

import propositional.basics tactic

namespace prop_logic

meta def prover_core : tactic unit :=
do
  goal ← tactic.target,
  match goal with
  | `(proof_of _ %%pf %%φ) := match pf with
                            | `(proof.ax1 _ _)   := tactic.applyc ``proof_of.ax1
                            | `(proof.ax2 _ _ _) := tactic.applyc ``proof_of.ax2
                            | `(proof.ax3 _)     := tactic.applyc ``proof_of.ax3
                            | `(proof.mp _ _)    := tactic.applyc ``proof_of.mp
                            | `(proof.assum _)   := do tactic.applyc ``proof_of.assum,
                                                       tactic.assumption
                            | _                  := tactic.assumption
                                                    <|>
                                                    tactic.tautology
                            end
  | _                    := tactic.fail "not a valid target"
  end

meta def prover : tactic unit :=
do
  tactic.repeat prover_core,
  tactic.done

lemma id_provable {α : Type} {φ : sentence α} {Γ : _} : Γ ⊢ (φ ↣ φ) :=
begin
  unfold provable,
  existsi (proof.mp (proof.mp (proof.ax2 φ (φ↣φ) φ)
                              (proof.ax1 φ (φ↣φ))) (proof.ax1 φ φ)),
  prover
end

lemma useless_condition {α : Type} {φ ψ : sentence α} {Γ : _} :
  Γ ⊢ φ → Γ ⊢ (ψ ↣ φ) :=
begin
  intro a,
  have pcut : provable Γ (φ ↣ ψ ↣ φ), {
    unfold provable, existsi proof.ax1 _ _, prover
  },
  cases a, cases pcut,
  existsi (proof.mp pcut_w a_w),
  prover
end

theorem deduction (α : Type) (φ ψ : sentence α) (Γ : _) :
  (φ::Γ) ⊢ ψ → Γ ⊢ (φ ↣ ψ) :=
begin
  intros a, unfold provable at a, cases a,
  induction a_h,
  repeat {
    apply useless_condition,
    {existsi proof.ax1 _ _, prover}
    <|> { existsi proof.ax2 _ _ _, prover }
    <|> { existsi proof.ax3 _, prover }
  },
  case mp {
    cases a_h_ih_a, cases a_h_ih_a_1,
    have ps : Γ ⊢ (Ax2 φ a_h_φ a_h_ψ), {
      unfold provable, existsi proof.ax2 _ _ _, prover
    }, cases ps, unfold Ax2 at ps_h,
    existsi proof.mp (proof.mp _ _) _,
    prover
  },
  case assum {
    induction a_h_a,
    case inl { rewrite ← a_h_a, apply id_provable },
    case inr {
      apply useless_condition,
      existsi proof.assum a_h_φ,
      prover,
    }
  }
end

lemma provable_with_stronger_assumption {α : Type} {φ : sentence α} {Γ Γ' : _} :
  Γ ⊆ Γ' → Γ ⊢ φ → Γ' ⊢ φ :=
begin
  intros a a', cases a',
  induction a'_h,
  case ax1 { existsi proof.ax1 _ _, prover },
  case ax2 { existsi proof.ax2 _ _ _, prover },
  case ax3 { existsi proof.ax3 _, prover },
  case mp { cases a'_h_ih_a, cases a'_h_ih_a_1, existsi proof.mp _ _, prover },
  case assum {
    have ht := a a'_h_a,
    existsi proof.assum _, prover
  }
end

lemma provable_with_append {α : Type} {φ : sentence α} {Γ : _} (ψ : sentence α) :
  Γ ⊢ φ → ψ::Γ ⊢ φ :=
begin
  have ht := list.subset_cons ψ Γ,
  intros a, apply provable_with_stronger_assumption,
  assumption, assumption
end

def provably_equivalent {α : Type} (Γ Γ' : list (sentence α)) : Prop :=
  ∀ φ, (Γ ⊢ φ ↔ Γ' ⊢ φ)

infix ≡ := provably_equivalent

lemma provable_append {α : Type} {Γ : _} {φ : sentence α} :
  Γ ⊢ φ → Γ ≡ φ::Γ :=
begin
  intros a φ', split,
  {
    intros a', cases a', induction a'_h,
    case ax1 { existsi proof.ax1 _ _, prover },
    case ax2 { existsi proof.ax2 _ _ _, prover },
    case ax3 { existsi proof.ax3 _, prover },
    case mp {
      cases a'_h_ih_a, cases a'_h_ih_a_1,
      existsi proof.mp _ _, prover
    },
    case assum {
      have ht' := list.mem_cons_of_mem φ a'_h_a,
      existsi proof.assum _, prover,
    }
  },
  {
    intros a', cases a', induction a'_h,
    case ax1 { existsi proof.ax1 _ _, prover },
    case ax2 { existsi proof.ax2 _ _ _, prover },
    case ax3 { existsi proof.ax3 _, prover },
    case mp {
      cases a'_h_ih_a, cases a'_h_ih_a_1,
      existsi proof.mp _ _, prover
    },
    case assum {
      cases a'_h_a,
      case or.inl { rewrite a'_h_a, assumption },
      case or.inr { existsi proof.assum _, prover }
    }
  }
end

lemma provable_contra {α : Type} {Γ : _} {φ : sentence α} :
  Γ ⊢ φ ↔ (~φ)::Γ ⊢ ⊥ :=
begin
  split,
  {
    intro a, simp,
    have ht' := provable_with_append (φ↣⊥) a,
    have htt := list.mem_cons_self (φ↣⊥) Γ,
    have ht2 : (φ↣⊥)::Γ ⊢ φ↣⊥, {existsi proof.assum _,  prover},
    cases ht', cases ht2,
    existsi proof.mp _ _, prover,
  },
  {
    intro a, simp at *,
    have ht : Γ ⊢ ~~φ, simp, apply deduction, assumption,
    have ht' : Γ ⊢ (~~φ)↣φ, {existsi proof.ax3 _, prover},
    simp at *, cases ht, cases ht',
    existsi proof.mp _ _, prover
  }
end

lemma contra_of_not_satis {α : Type} {Γ : list (sentence α)} :
  contradiction_set Γ → ¬ satisfiable_set Γ :=
begin
  unfold contradiction_set satisfiable_set,
  intros, apply not_exists_of_forall_not, intros V,
  apply not_forall_of_exists_not, cases (a V), existsi w, tauto
end

lemma not_satis_of_contra {α : Type} {Γ : list (sentence α)} :
  ¬ satisfiable_set Γ → contradiction_set Γ :=
begin
  unfold contradiction_set satisfiable_set,
  intros, have a' := forall_not_of_not_exists a V,
  simp at a', cases a' with φ h,
  existsi φ, assumption
end

lemma inside_provable {α : Type} {Γ : _} {φ : sentence α} :
  φ ∈ Γ → Γ ⊢ φ :=
begin
  intros, existsi proof.assum _, prover
end

theorem appending_unprovable {α : Type} {Γ : _} {φ : sentence α} :
  ¬(Γ ⊢ φ) → consistent ((~φ)::Γ) :=
begin
  intros a contra, simp at *,
  have ht := provable_contra.mpr contra,
  exact a ht
end

theorem appending_provable {α : Type} {Γ : _} {φ : sentence α} :
  consistent Γ → Γ ⊢ φ → consistent (φ::Γ) :=
begin
  intros a a', unfold consistent at *,
  intro contra,
  exact a ((provable_append a' ⊥).mpr contra)
end

theorem explosion {α : Type} {Γ : list (sentence α)} {φ : sentence α} :
  Γ ⊢ ⊥ → Γ ⊢ φ :=
begin
  intros a,
  apply provable_contra.mpr, simp,
  exact provable_with_append _ a
end

lemma false_condition {α : Type} {Γ : _} {φ ψ : sentence α} :
  Γ ⊢ (φ ↣ ψ) ↣ ⊥ → Γ ⊢ φ ↣ ⊥ → Γ ⊢ ⊥ :=
begin
  intros a a',
  have ht : Γ ⊢ φ ↣ ψ, {
    apply deduction,
    have ht₁ := provable_with_append φ a',
    have ht₂ : φ ∈ (list.cons φ Γ) := by simp,
    have ht₃ : φ :: Γ ⊢ φ, { existsi proof.assum _, prover },
    have ht₄ : φ :: Γ ⊢ ⊥, {
      cases ht₁, cases ht₃, existsi proof.mp _ _, prover
    },
    apply explosion, assumption
  },
  cases a, cases ht, existsi proof.mp _ _, prover
end

end prop_logic


import .set .list
namespace cat

universes u v

class has_hom (obj : Type u) : Type (max u (v + 1)) :=
(hom : obj → obj → Type v)
infixr ` ⟶ `:10 := has_hom.hom -- type as \h
@[inline] def hom (obj : Type u) [has_hom obj] (X : obj) (Y : obj) := @has_hom.hom obj _ X Y

class has_id_comp (obj : Type u) extends has_hom obj :=
(id : Π {X : obj}, X ⟶ X)
(comp : Π {X Y Z : obj}, (X ⟶ Y) → (Y⟶ Z) → (X ⟶Z))
@[inline] def comp (obj : Type u) [has_id_comp obj] {X Y Z : obj} := @has_id_comp.comp obj _ X Y Z
@[reducible] def has_id_comp_id {obj : Type u} [has_id_comp obj] {X : obj} : X ⟶ X := has_id_comp.id obj
@[reducible] def has_id_comp_id_exp {obj : Type u} [has_id_comp obj] (X : obj) : X ⟶ X := has_id_comp.id obj
notation `𝟙` := has_id_comp_id -- type as \b1
notation `𝟙_`X := has_id_comp_id_exp X -- type as \b1
infixr ` ≫ `:80 := has_id_comp.comp -- type as \gg

class category (obj : Type u) extends has_id_comp obj :=
-- (hom      : obj → obj → Type v)
-- (id       : Π X : obj, hom X X)
-- (comp     : Π {X Y Z : obj}, hom X Y → hom Y Z → hom X Z)
(id_comp : ∀ {X Y : obj} (f : X ⟶ Y), 𝟙 ≫ f = f)
(comp_id : ∀ {X Y : obj} (f : X ⟶ Y), f ≫ 𝟙 = f)
(assoc   : ∀ {W X Y Z : obj} (f : W ⟶ X) (g : X ⟶ Y) (h : Y ⟶ Z), (f ≫ g) ≫ h = f ≫ (g ≫ h))

attribute [simp] category.id_comp category.comp_id category.assoc

universes u₁ v₁ u₂ v₂ u₃ v₃ u₄ v₄

section iso_epi_mono
  variables {C : Type u₁} [𝒞 : category.{u₁ v₁} C] {X Y Z : C}
  include 𝒞
  def is_epic (f : X ⟶ Y) := ∀ (g₁ g₂ : Y ⟶ Z), f ≫ g₁ = f ≫ g₂ → g₁ = g₂
  def is_monic (f : Y ⟶ Z) := ∀ (g₁ g₂ : X ⟶ Y), g₁ ≫ f =  g₂ ≫ f → g₁ = g₂
  -- idea: make constructive?
  def is_iso (f : X ⟶ Y) := ∃ (g : Y ⟶ X), f ≫ g = 𝟙 ∧ g ≫ f = 𝟙
  structure iso (X Y : C) :=
  (f : X ⟶ Y)
  (g : Y ⟶ X)
  (fg_id : f ≫ g = 𝟙)
  (gf_id : g ≫ f = 𝟙)
  infixr ` ⟷ ` :10 := iso
  instance : has_coe (@iso C 𝒞 X Y) (X ⟶ Y) := ⟨λ x, x.f⟩
end iso_epi_mono

section
structure functor (C : Type u₁) [category.{u₁ v₁} C] (D : Type u₂) [category.{u₂ v₂} D] : Type (max u₁ v₁ u₂ v₂) :=
(obj       : C → D)
(map      : Π {X Y : C}, (X ⟶ Y) → ((obj X) ⟶ (obj Y)))
(map_id   : ∀ (X : C), map (𝟙 : X ⟶ X) = 𝟙)
(map_comp : ∀ {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z), map (f ≫ g) = (map f) ≫ (map g))
attribute [simp] functor.map_id functor.map_comp

infixr ` ⥤ `:70 := functor       -- type as \func --
instance {C : Type u₁} [category.{u₁ v₁} C] {D : Type u₂} [category.{u₂ v₂} D] : has_coe_to_fun (C ⥤ D) :=
{ F   := λ F, C → D,
  coe := λ F, F.obj }
end
lemma functor.ext
  {C : Type u₁} [category.{u₁ v₁} C] {D : Type u₂} [category.{u₂ v₂} D] :
  Π {F G : C ⥤ D}
  (oe : F.obj = G.obj)
  (me : ((eq.rec_on oe F.map) : Π {X Y : C}, (X ⟶ Y) → ((G.obj X) ⟶ (G.obj Y))) = G.map), F = G
  |⟨o,m,_,_⟩ ⟨_,_,_,_⟩ rfl rfl := rfl
namespace functor
  section
    variables {C : Type u₁} [𝒞 : category.{u₁ v₁} C]
              {D : Type u₂} [𝒟 : category.{u₂ v₂} D]
    include 𝒞 𝒟

    --@[simp] lemma functor_map_id {F : C ⥤ D} {X : C} : F.map (𝟙 X) = 𝟙 (F.obj X) := F.map_id _

    variables {E : Type u₃} [ℰ : category.{u₃ v₃} E]
    include ℰ
    /--
    `F ⋙ G` is the composition of a functor `F` and a functor `G` (`F` first, then `G`).
    -/
    def comp
        (F : C ⥤ D) (G : D ⥤ E) : C ⥤ E :=
    { obj      := λ X, G (F X)
    , map      := λ _ _ f, G.map (F.map f)
    , map_id   := λ X,
      calc G.map (F.map (𝟙 : X ⟶ X)) = G.map (𝟙) : by rw [F.map_id]
                                  ... = 𝟙 : by rw [G.map_id]
    , map_comp := λ X Y Z f g, begin simp [functor.map_comp] end
    }
    infixr ` ⋙ `:80 := functor.comp
    @[simp] lemma comp_obj_expand {F : C ⥤ D} {G : D ⥤ E} {X : C} : (F ⋙ G).obj X = G.obj (F.obj X) := rfl
    @[simp] lemma comp_map_expand {F : C ⥤ D} {G : D ⥤ E} {X Y : C} {f : X ⟶ Y}: (F ⋙ G).map f = G.map (F.map f)
    := begin refl end
  end

  def id (C : Type u₁) [𝒞 : category.{u₁ v₁} C] : C ⥤ C :=
  { obj := λ X, X
  , map := λ _ _ f, f
  , map_id := λ X, rfl
  , map_comp := λ X Y Z f g, rfl
  }
  @[simp] lemma  id_obj_expand (C : Type u₁) [𝒞 : category.{u₁ v₁} C] {X : C} : (functor.id C).obj X = X := rfl
  @[simp] lemma  id_map_expand (C : Type u₁) [𝒞 : category.{u₁ v₁} C] {X Y : C} {f : X ⟶ Y} : (functor.id C).map f = f := rfl

  lemma comp_id
      {C : Type u₁} [𝒞 : category.{u₁ v₁} C]
      {D : Type u₂} [𝒟 : category.{u₂ v₂} D]
      : ∀ (F : C ⥤ D), (F ⋙ (functor.id D)) = F :=
      λ F, begin apply functor.ext _ _, reflexivity, reflexivity end

  lemma id_comp
      {C : Type u₁} [𝒞 : category.{u₁ v₁} C]
      {D : Type u₂} [𝒟 : category.{u₂ v₂} D]
      : ∀ (F : C ⥤ D), ((functor.id C)⋙F) = F :=
      λ F, begin apply functor.ext _ _, reflexivity, reflexivity end

  lemma assoc
      {C₁ : Type u₁} [𝒞₁ : category.{u₁ v₁} C₁]
      {C₂ : Type u₂} [𝒞₂ : category.{u₂ v₂} C₂]
      {C₃ : Type u₃} [𝒞₃ : category.{u₃ v₃} C₃]
      {C₄ : Type u₄} [𝒞₄ : category.{u₄ v₄} C₄]
      : ∀ (F₁₂ : C₁ ⥤ C₂) (F₂₃ : C₂ ⥤ C₃) (F₃₄ : C₃ ⥤ C₄), F₁₂ ⋙ (F₂₃ ⋙ F₃₄) = (F₁₂ ⋙ F₂₃) ⋙ F₃₄  :=
      begin intros, apply functor.ext _ _, reflexivity, reflexivity end

end functor
open functor

/-- The category of small categories -/
structure Cat : Type (max (u+1) (v+1)) :=
(ob : Type u) [cat : category.{u v} ob]
instance (𝒞 : Cat) : category (𝒞.ob) := 𝒞.cat
instance Cat_is_category : category.{(max (u+1) (v+1)) (max u v)} Cat :=
{ hom := λ 𝒞 𝒟, 𝒞.ob ⥤ 𝒟.ob
, id  := λ 𝒞, functor.id 𝒞.ob
, comp := λ 𝒞 𝒟 ℰ F G, F ⋙ G
, id_comp := λ 𝒞 𝒟 F, functor.id_comp _
, comp_id := λ 𝒞 𝒟 F, functor.comp_id _
, assoc := begin intros, apply functor.assoc end
}

structure nat_trans
  {C : Type u₁} {D : Type u₂} [category.{u₁ v₁} C] [category.{u₂ v₂} D]
  (F G : C ⥤ D) : Type (max u₁ v₂) :=
(app : Π X : C, (F.obj X) ⟶ (G.obj X))
(naturality : ∀ {X Y : C} (f : X ⟶ Y), (F.map f) ≫ (app Y) = (app X) ≫ (G.map f))
attribute [simp] nat_trans.naturality

infixr ` ⟹ `:50  := nat_trans             -- type as \==> or ⟹

namespace nat_trans
  variables {C : Type u₁} {D : Type u₂} [𝒞 : category.{u₁ v₁} C] [𝒟 : category.{u₂ v₂} D]
  variables {F G H I : C ⥤ D}
  include 𝒞 𝒟
  instance : has_coe_to_fun (F ⟹ G) :=
  { F   := λ _, Π X:C, F X ⟶ G X, coe := λ τ, τ.app}

  lemma ext : ∀ {τ σ : F ⟹ G} (p : τ.app = σ.app), τ = σ
  |⟨a,_⟩ ⟨_,_⟩ rfl := rfl

  def id : F ⟹ F :=
  { app := λ X, 𝟙_(F.obj X)
  , naturality := λ X Y f, by simp
  }
  @[simp] lemma reduce_id {X : C} : nat_trans.id.app X = 𝟙_(F.obj X) := rfl

  def vcomp (α : F ⟹ G) (β : G ⟹  H) : F ⟹ H :=
  {app := λ X, α.app X ≫ β.app X
  , naturality := begin intros, rw [<-category.assoc], rw [naturality], simp end
  }

  notation α ` ⊟ ` β:80 := vcomp α β -- I usually just use ≫

  instance Hom_is_category : category.{(max u₁ v₁ u₂ v₂) (max u₁ v₂)} (C ⥤ D) :=
  { hom := λ F G, F ⟹ G
  , id := λ F, nat_trans.id
  , comp := λ F G H α β, vcomp α β
  , id_comp := begin intros, apply ext, simp[vcomp, id]end
  , comp_id := begin intros, apply ext, simp[vcomp, id]end
  , assoc := begin intros, apply ext, simp [vcomp] end
  }

  @[simp] lemma vcomp_reduce {α : F ⟹ G} {β : G ⟹ H} : (α ⊟ β).app = λ X, (α.app X)≫ (β.app X) := rfl
  @[simp] lemma nat_trans_comp_reduce {α : F ⟶ G} {β : G ⟶ H} : (α ≫ β).app = λ X, (α.app X)≫ (β.app X) := rfl
  @[simp] lemma nat_trans_id_reduce : (𝟙 : F ⟶ F).app = λ X, 𝟙 := rfl

  def hcomp
    {E : Type u₃} [category.{u₃ v₃} E]
    {I J : D ⥤ E}
    (α : F ⟹ G) (β : I ⟹ J) : (F ⋙ I) ⟹ (G ⋙ J) :=
  { app := λ X, I.map (α.app X) ≫ β.app (G.obj X)
  , naturality := begin intros, simp, rw [<-category.assoc], rw [naturality], rw [category.assoc], rw [<-functor.map_comp], rw [naturality], rw [functor.map_comp] end
  }

  notation α `◫` β : 80 := hcomp α β

  /- [TODO]
  - [ ] whiskering
  - [ ] hcomp makes Cat a 2-category.
  -/
end nat_trans
open nat_trans

section product
  variables {C : Type u₁} {D : Type u₂} [𝒞 : category.{u₁ v₁} C] [𝒟 : category.{u₂ v₂} D]
  include 𝒞 𝒟
  instance product_is_cat
    : category.{(max u₁ u₂) (max v₁ v₂)} (C × D) :=
  { hom := λ x y, (x.1 ⟶ y.1) × (x.2 ⟶ y.2)
  , id  := λ p, ⟨𝟙, 𝟙⟩
  , comp := λ x y z f g, ⟨f.1 ≫ g.1, f.2 ≫ g.2⟩
  , id_comp := begin intros, simp end
  , comp_id := begin intros, simp end
  , assoc := begin intros, simp end
  }
  attribute [reducible] cat.product_is_cat
  variables {X : C × D}
  --@[simp] lemma product_id_fst : (𝟙_X).1 = 𝟙_(X.1) :=
  @[simp] lemma product_id : (𝟙_X) = (𝟙_X.fst, 𝟙_X.snd) := rfl
  @[simp] lemma product_comp_fst
  {X Y Z: C × D} {f : X ⟶ Y} {g : Y ⟶ Z} : (f ≫ g).fst = f.fst ≫ g.fst := rfl
  @[simp] lemma product_comp_snd
  {X Y Z: C × D} {f : X ⟶ Y} {g : Y ⟶ Z} : (f ≫ g).snd = f.snd ≫ g.snd := rfl
  attribute [simp] cat.product_is_cat
  def fst_functor : (C × D) ⥤ C :=
  {obj := λ P, P.1, map := λ P Q f, f.1, map_id := λ P, rfl, map_comp := λ P Q R f g , rfl}
end product
section product_coe
    variables {C₁ : Type u₁} [𝒞₁ : category.{u₁ v₁} C₁]
              {C₂ : Type u₂} [𝒞₂ : category.{u₂ v₂} C₂]
              {C₃ : Type u₃} [𝒞₃ : category.{u₃ v₃} C₃]
              {C₄ : Type u₄} [𝒞₄ : category.{u₄ v₄} C₄]
  include 𝒞₁ 𝒞₂ 𝒞₃ 𝒞₄
  def prod_functor_of_functor_prod : ((C₁ ⥤ C₂) × (C₃ ⥤ C₄)) → ((C₁ × C₃) ⥤ (C₂ × C₄)) :=λ F, {
     obj := λ p, (F.1.obj p.1, F.2.obj p.2)
    , map := λ P Q p, (F.1.map p.1,F.2.map p.2)
    , map_id := λ P, begin simp end
    , map_comp := begin intros, simp end
   }
  instance functor_prod
   : has_coe ((C₁ ⥤ C₂) × (C₃ ⥤ C₄)) ((C₁ × C₃) ⥤ (C₂ × C₄)) := ⟨prod_functor_of_functor_prod⟩
end product_coe
section product_adj
  variables {C : Type u₁} [𝒞 : category.{u₁ v₁} C]
            {D : Type u₂} [𝒟 : category.{u₂ v₂} D]
            {E : Type u₃} [ℰ : category.{u₃ v₃} E]
  include 𝒞 𝒟 ℰ

  def product_adj : (C × D) ⥤ E → C ⥤ (D ⥤ E) := λ F,
  { obj := λ c,
    { obj := λ d, F.obj ⟨c,d⟩
    , map := λ d₁ d₂ f, F.map (𝟙, f)
    , map_id := λ d, begin rw [<-functor.map_id] end
    , map_comp := λ d₁ d₂ d₃ f g, begin rw [<-functor.map_comp], apply congr_arg, simp end
    }
  , map := λ c₁ c₂ f, nat_trans.mk (λ d, F.map ⟨f, 𝟙⟩)
      begin intros d₁ d₂ g, simp [functor.map],  rw [<-functor.map_comp],  rw [<-functor.map_comp], simp  end
  , map_id := λ c, nat_trans.ext $ funext $ λ d,
      show F.map (𝟙 : (c,d) ⟶ (c,d)) = 𝟙_(F.obj (c, d)), by simp
  , map_comp := λ c₁ c₂ c₃ f g, nat_trans.ext $ funext $ λ d,
      show F.map ((f ≫ g, 𝟙) : (c₁,d) ⟶ (c₃,d)) = F.map ((f, 𝟙): (c₁,d) ⟶ (c₂,d)) ≫ F.map (g, 𝟙),
      begin rw <- functor.map_comp, apply congr_arg, simp end
  }

end product_adj

section op
  def op (C : Type*) := C
  variables {C : Type u₁} [𝒞 :category.{u₁ v₁} C]
  include 𝒞
  instance op_is_cat  : category.{u₁ v₁} (op C) :=
  { hom := λ X Y, @hom C (𝒞.to_has_hom) Y X -- just to make sure it's using the correct one
  , id := @has_id_comp.id C _
  , comp := λ X Y Z f g, @has_id_comp.comp C _ _ _ _ g f
  , id_comp := begin intros, simp end
  , comp_id := begin intros, simp end
  , assoc   := begin intros, simp end
  }
  #check @cat.op_is_cat
  attribute [simp] op
  attribute [simp] cat.op_is_cat
  notation C `ᵒᵖ` := op C
  @[inline] def hom_of_op_hom {X Y : op C} : hom (op C) X Y → hom C Y X := λ f, f
  @[inline] def op_hom_of_hom {X Y : op C} : hom (C) X Y → hom (op C) Y X := λ f, f
  @[simp] lemma op_hom_elim {Y X : op C} :
    hom (op C) X Y = hom C Y X := rfl
  @[simp] lemma op_comp
    {Z Y X : op C} {f : hom (op C) X Y} {g : hom (op C) Y Z}:
    (@has_id_comp.comp (op C) _ _ _ _ f g) = @has_id_comp.comp C _ Z Y X (@hom_of_op_hom C _ Y Z g) (@hom_of_op_hom C _ X Y f) := rfl
  variables {D : Type u₂} [𝒟 :category.{u₂ v₂} D]
  include 𝒟

  def functor_op (F : C ⥤ D) : Cᵒᵖ ⥤ (Dᵒᵖ) :=
  { obj := λ c, F.obj c
  , map := λ X Y f,  F.map (f : hom C Y X)
  , map_id := begin intros, apply functor.map_id end
  , map_comp := begin intros, apply functor.map_comp end
  }
  notation F`ᵒᵖ` := functor_op F
end op

section unit
  instance unit_is_cat : category (unit) :=
  { hom := λ X Y, unit
  , id := λ X, ⟨⟩
  , comp := λ _ _ _ _ _, ⟨⟩
  , id_comp := begin intros, cases f, refl end
  , comp_id := begin intros, cases f, refl end
  , assoc := begin intros, cases f, cases g, cases h, refl end
  }
  inductive equaliser_shape : Type |A|B
  inductive span_shape : Type |L | M | R

end unit

instance Sort_is_cat : category.{(u+1) (u)} (Type u) :=
{ hom := λ X Y, X → Y
, id := λ X x, x
, comp := λ X Y Z f g x, g (f x)
, id_comp := λ X Y f, rfl
, comp_id := λ X Y f, rfl
, assoc := λ X Y Z W f g h, rfl
}
@[simp] lemma Sort_is_cat_id {α : Type u} : (𝟙_α) = id := by refl
@[simp] lemma Sort_is_cat_comp {α β γ : Type u} {f : α ⟶ β} {g : β ⟶ γ} : (f ≫ g) = g ∘ f := by refl

def Hom {C : Type u} [𝒞 : category.{u v} C] : ((C ᵒᵖ) × C) ⥤ Type v :=
{ obj := λ P, hom C P.1 P.2
, map := λ P Q fg (p), fg.1 ≫ p ≫ fg.2
, map_id := begin intros, funext, simp, apply category.id_comp end
, map_comp := begin intros, funext, simp, refl end
}

section comma
  variables {C : Type u₁} [𝒞 : category.{u₁ v₁} C]
            {D : Type u₂} [𝒟 : category.{u₂ v₂} D]
            {E : Type u₃} [ℰ : category.{u₃ v₃} E]
  include 𝒞 𝒟 ℰ
  structure comma (F : C ⥤ E) (G : D ⥤ E) :=
  (o1 : C) (o2 : D) (f : F o1 ⟶ G o2)
  variables {F : C ⥤ E} {G : D ⥤ E}
  structure comma_hom (X Y : comma F G) :=
  (g1 : X.o1 ⟶ Y.o1)
  (g2 : X.o2 ⟶ Y.o2)
  (nat : F.map g1 ≫ Y.f = X.f ≫ G.map g2)
  def comma_hom.ext {X Y : comma F G} : Π {f g : comma_hom X Y}, f.g1 = g.g1 → f.g2 = g.g2 → f = g
  |⟨g1,g2,_⟩ ⟨_,_,_⟩ rfl rfl := rfl
  attribute [simp] comma_hom.nat
  instance comma.category : category (comma F G) :=
  { hom := λ X Y, comma_hom X Y
  , id := λ X, comma_hom.mk (𝟙) (𝟙) (by simp)
  , comp := λ X Y Z a b, ⟨a.g1 ≫ b.g1, a.g2 ≫ b.g2, begin simp, rw [<-category.assoc, comma_hom.nat], simp end⟩
  , id_comp := begin intros, simp, apply comma_hom.ext, simp end
  , comp_id := begin intros, simp, apply comma_hom.ext, simp end
  , assoc := begin intros, apply comma_hom.ext, simp, simp end
  }
end comma

section slice
    def slice (C : Type u₁) [category.{u₁ v₁} C] (Y : C) := Σ X : C, X ⟶ Y
    variables {C : Type u₁} [𝒞 : category.{u₁ v₁} C] {X Y : C}
    include 𝒞
    notation C `/` X := slice C X
    instance slice.category : category (C / Y) :=
    { hom := λ A B, {f : A.1 ⟶ B.1 // f ≫ B.2 = A.2}
    , id := λ A, ⟨𝟙_A.1, by simp⟩
    , comp := λ f g h p q, ⟨p.1 ≫ q.1, by simp [p.2,q.2]⟩
    , id_comp := begin intros, simp end
    , comp_id := begin intros, simp end
    , assoc := begin intros, simp end
    }
    --@[simp] lemma slice.category_id {f : C / Y} : (@category.id (C / Y) _ f).1 = 𝟙 (f.1) := rfl
    --@[simp] lemma slice.category_comp {f g h : C / Y} {p : f ⟶ g} {q : g ⟶ h} : (p ≫ q).1 = p.1 ≫ q.1 := rfl
    def slice.forget : C / X ⥤ C :=
    { obj := λ f, f.1
    , map := λ f g p, p.1
    , map_id := begin intros, refl end
    , map_comp := begin intros, refl end
    }
end slice

section adjoint
  variables {C : Type u₁} {D : Type u₂} [𝒞 : category.{u₁ v₁} C] [𝒟 :category.{u₂ v₂} D]
  include 𝒞 𝒟
  class adjunction
    (L : C ⥤ D) (R : D ⥤ C) :=
    (unit       : functor.id C ⟹ (L ⋙ R))
    (counit     : (R ⋙ L) ⟹ functor.id D)
    (triangle_1 : ∀ X : D, (unit (R X)) ≫ (R.map (counit X)) = 𝟙_(R X))
    (triangle_2 : ∀ X : C, (L.map (unit X)) ≫ (counit (L X)) = 𝟙_(L X))
  attribute [simp] adjunction.triangle_1 adjunction.triangle_2
  --def adjunction_alt (L : C ⥤ D) (R : D ⥤ C) := -- [ERROR] This only works in small categories?
  --   ((Lᵒᵖ, D) ⋙ Hom) ⟷ ((Cᵒᵖ, D) ⋙ Hom)   -- It would be great if the parser could figure this out!
  infixr ` ⊣ `:70 := adjunction
  variables {L : C ⥤ D} {R : D ⥤ C} [L ⊣ R] {c : C} {d : D}
  /--Shorthand for unit of adjoint -/
  def adjunction.μ := adjunction.unit L R c
  /-- Shorthand for counit of adjoint -/
  def adjunction.ε := adjunction.counit L R d
  open adjunction
  def adjunction.φ : (c ⟶ R d) → (L c ⟶ d) := λ f, (L.map f) ≫ ε
  def adjunction.ψ : (L c ⟶ d) → (c ⟶ R d) := λ g, μ ≫ (R.map g)
  -- [TODO] show they are isos.
end adjoint

section alternative_adjoint
  -- another definition. In theory this one should be pithier but you have to hold the elaborator's hand.
  def adj_alt {C D : Type u} [𝒞 : category.{u v} C] [𝒟 : category.{u v} D] (L : C ⥤ D) (R : D ⥤ C)
  :=
  --   ((Lᵒᵖ, D) ⋙ Hom) ⟷ ((Cᵒᵖ, D) ⋙ Hom)   -- It would be great if the parser could figure this out!
    have L3 : (((Cᵒᵖ) × D) ⥤ (Type v)) := @functor.comp ((Cᵒᵖ) × D) _ ((Dᵒᵖ) × D) _ (Type v) _ (Lᵒᵖ,functor.id D) (@Hom D 𝒟),
    have L4 : (((Cᵒᵖ) × D) ⥤ (Type v)) := @functor.comp ((Cᵒᵖ) × D) _ ((Cᵒᵖ) × C) _ (Type v) _ (functor.id (Cᵒᵖ), R) (@Hom C 𝒞),
    have category (((Cᵒᵖ) × D) ⥤ (Type v)) := begin apply @nat_trans.Hom_is_category end,
    @iso _ this L3 L4
    -- [ERROR] the elaborator can't figure out that ((Cᵒᵖ) × D) ⥤ (Type v) is a category.
  -- def adj_of_adj_alt {C D : Type u} [𝒞 : category.{u v} C] [𝒟 : category.{u v} D] {L : C ⥤ D}{R : D ⥤ C}
  -- : adj_alt L R → adjunction L R := λ i,
  --   { unit := {app := λ X,
  --     begin cases i with φ ψ h₁ h₂, simp * at *, simp [has_hom.hom] at φ,
  --     have φa : (L.obj X ⟶ L.obj X) → (X ⟶ R.obj (L.obj X)),
  --       apply @nat_trans.app,
  --     have φa := φ.app (X, L.obj X),     end /- i.g.app (X,L.obj X) 𝟙 -/, naturality := sorry}
  --   , counit := {app := λ X, i.f.app (R.obj X, X) 𝟙, naturality := sorry}
  --   , triangle_1 := sorry
  --   , triangle_2 := sorry
  --   }
end alternative_adjoint

section simple_limits
    variables {C : Type u₁} [𝒞 : category.{u₁ v₁} C] {X Y Z: C}
    include 𝒞

    notation X` ⇉ ` Y := (X ⟶ Y) × (X ⟶ Y)

    structure initial :=
    (O : C) (univ : ∀ (X : C), ∃! (o : O ⟶ X), true)

    structure equaliser (p: X ⇉ Y) :=
    (E : C) (e : E ⟶ X)
    (same : e ≫ p.1 = e ≫ p.2)
    (univ : ∀ (A : C) (f : A ⟶ X), (f ≫ p.1 = f ≫ p.2) → ∃! (k : A ⟶ E), k ≫ e = f)

    structure pullback (f : X ⟶ Z) (g : Y ⟶ Z) :=
    (L : C)
    (π₁ : L ⟶ X)
    (π₂ : L ⟶ Y)
    (square : π₁ ≫ f = π₂ ≫ g)
    (univ : ∀ (A : C) (a₁ : A ⟶ X) (a₂ : A ⟶ Y), (a₁ ≫ f) = (a₂ ≫ g) → ∃! (k : A ⟶ L), k ≫ π₁ = a₁ ∧ k ≫ π₂ = a₂)

    -- COLIMITS. I am just going to copy out the definitions because using Cᵒᵖ is masochistic.

    structure terminal :=
    (I : C) (univ : ∀ (X : C), ∃! (i : X ⟶ I), true)

end simple_limits

section set_function_cat

  /-- The category of functions on sets of α -/
  instance cat_of_set {α : Type u} : category (set α) :=
  {  hom := λ X Y, {x // x ∈ X} → {y // y ∈ Y}
  , id := λ X x, x
  , comp := λ X Y Z p q x, q $ p $ x
  , comp_id := begin intros, simp end
  , id_comp := begin intros, simp end
  , assoc := begin intros, simp end
  }

end set_function_cat

namespace free_cat
      def free (α : Type u) [hh :has_hom.{u v} α] := α

      variables {α : Type u} [hh : has_hom.{u v} α]
      include hh

      inductive fh : free α → free α → Type (max u v)
      |basic {a b : free α} : (@has_hom.hom α hh a b) → fh a b
      |id : Π {a}, fh a a
      |comp {a b c : free α} : fh a b → fh b c → fh a c
      open fh
      local infix ` ⇢ ` :70 := fh
      inductive hom_rel : Π {a b :free  α}, (a ⇢ b) → fh a b → Prop
      |comp_id {a b} {f : fh a b} : hom_rel (fh.comp f (id)) f
      |id_comp {a b} {f : fh a b} : hom_rel (fh.comp id f) f
      |assoc {a b c d} {f : fh a b} {g : fh b c} {h : fh c d} : hom_rel (fh.comp (fh.comp f g) h) (fh.comp f (fh.comp g h))
      |refl {a b} {f : a ⇢ b} : hom_rel f f
      |trans {a b} {f g h : a ⇢ b} : hom_rel f g → hom_rel g h → hom_rel f h
      |symm {a b} {f g : a ⇢ b} : hom_rel f g → hom_rel g f
      |congr {a b c} {f₁ f₂ : a ⇢ b} {g₁ g₂ : b ⇢ c} : hom_rel f₁ f₂ → hom_rel g₁ g₂ → hom_rel (fh.comp f₁ g₁) (fh.comp f₂ g₂) -- [?] do I actually need this one?

    def fhs (a b: free α) : setoid (fh a b) :=
    { r := λ f g, hom_rel f g
    , iseqv := ⟨λ f, hom_rel.refl,λ f g, hom_rel.symm,λ f g h, hom_rel.trans⟩
    }
    /-- The free category over some generating signature of homs. -/
    instance free_is_category {α : Type u} [hh : has_hom.{u v} α]: category.{u (max u v)} (free α) :=
    { hom := λ a b, quotient $ fhs a b
    , id := λ a, @quotient.mk (fh a a) (fhs a a) (@fh.id _ _ a)
    , comp := λ a b c f g, @quotient.lift_on₂ _ _ _ (fhs a b) (fhs b c) f g (λ (f : a⇢b) (g:b⇢c), @quotient.mk _ (fhs a c) $ fh.comp f g)
        $ λ a₁ a₂ b₁ b₂ ab₁ ab₂, @quotient.sound _ (fhs a c) _ _ $ hom_rel.congr ab₁ ab₂
        --begin  apply hom_rel.congr   end
    , id_comp := begin intros, induction f, apply quotient.sound, apply hom_rel.id_comp, refl end
    , comp_id := begin intros, induction f, apply quotient.sound, apply hom_rel.comp_id, refl end
    , assoc := begin intros, induction f, induction g, induction h, apply quotient.sound, apply hom_rel.assoc, repeat {refl} end
    }
    instance {a b : α} : has_coe (@has_hom.hom α hh a b) (@has_hom.hom (free α) (free_cat.free_is_category.to_has_hom) a b)
    := ⟨λ x, @quotient.mk _ (fhs a b) $ fh.basic $ x⟩
    --export free [TODO] how to expose free?
end free_cat

namespace shape

  /-- The category `∙→∙` of two objects and one arrow. -/
  def two := set unit -- either ∅ or {*}
  instance : category (two) := cat.cat_of_set

  inductive span_sig |A | B | X
  open span_sig
  inductive span_hom : span_sig → span_sig → Type
  |a : span_hom A X
  |b : span_hom B X
  instance : has_hom (span_sig) := ⟨λ Y Z, span_hom Y Z⟩
  def span := free_cat.free (span_sig)

end shape

section limit
  variables {J : Type u₁} [𝒥 : category.{u₁ v₁} J] {i j k: J} -- [TODO] non-small categories
  variables {C : Type u₂} [𝒞 : category.{u₂ v₂} C] {X Y Z: C}
  include 𝒥 𝒞
  /-- The constant diagram functor.  -/
  @[reducible] def Δ : C ⥤ (J ⥤ C) :=
  { obj := λ X,
    { obj := λ i, X
    , map := λ _ _ _, 𝟙
    , map_id := λ j, rfl
    , map_comp := by intros; simp
    }
  , map := λ X Y f,
    { app := λ _, f
    , naturality := λ i j ij, by simp
    }
  , map_comp := begin intros, apply nat_trans.ext, simp end
  , map_id := begin intros, apply nat_trans.ext, simp end
  }
  --@[simp] lemma wtf : (Δ.obj X: J ⥤ C) = {obj := λ i, X, map := λ i j ij, 𝟙 X,  map_id := λ j, rfl, map_comp := by intros; simp} := begin refl end
  -- @[simp] lemma Δ_def : (Δ : C ⥤ (J ⥤ C)) = product_adj (fst_functor) := rfl
  @[simp] lemma Δ_obj :(Δ.obj X).obj j = X := rfl
  @[simp] lemma Δ_map (ij : i ⟶ j) :(Δ.obj X).map ij = 𝟙 := rfl
  /-- The limit functor for a particular shape J. This is inhabited when the limit exists for all diagrams. -/
  class has_limits (J : Type u₁) [category.{u₁ v₁} J] (C : Type u₂) [category.{u₂ v₂} C]
  := (Lim : (J ⥤ C) ⥤ C) (Δ_adj_Lim : Δ ⊣ Lim)

  structure cone (d : J ⥤ C) :=
  (peak : C)
  (legs : (Δ.obj peak) ⟶ d)

  def is_limit {d : J ⥤ C} (c : cone d)
  := ∀ (A : C) (α : (Δ.obj A) ⟶ d), ∃! (e : A ⟶ c.peak), α = ((Δ.map e) ≫ c.legs)

  section preserves_etc
    variables {D : Type u₃} [𝒟 : category.{u₃ v₃} D]
    include 𝒟
    open has_limits
    def map_cone {d : J ⥤ C} (F : C ⥤ D) (l : cone d) : cone (d ⋙ F) :=
    {peak := F.obj l.peak
    , legs :=
      { app := λ j, begin cases l with Lc πc, apply F.map, apply πc.app end
      , naturality := begin intros i j ij, cases l, simp, rw [<-functor.map_comp, <-nat_trans.naturality], simp end
    }}
    variables (F : C ⥤ D) (d : J ⥤ C)
    def preserves_limits := ∀ (l : cone d) (hl : is_limit l), is_limit (map_cone F l)
    def reflects_limits := ∀ (l : cone d) (hl : is_limit (map_cone F l)), is_limit l
    def creates_limits := ∀ (l : cone (d ⋙ F)), is_limit l → ∃ l' : cone d, l = map_cone F l'

  end preserves_etc

end limit

section nat_cat
  instance nat_is_category : category (nat) :=
  { hom := λ n m, {s:unit // n ≤ m}
  , id := λ n, ⟨⟨⟩, by refl⟩
  , comp := λ i j k f g, ⟨⟨⟩, begin cases f, cases g, transitivity, assumption, assumption end⟩
  , id_comp:=  λ i k ⟨⟨⟩,f⟩, rfl
  , comp_id:= λ i k ⟨⟨⟩,f⟩, rfl
  , assoc := λ i j k l ⟨⟨⟩,f⟩ ⟨⟨⟩,g⟩ ⟨⟨⟩,h⟩, rfl
  }
end nat_cat

section simplicial
  def simplex := nat
  open nat

  def simp_comp_aux : list nat → list nat → nat → list nat → list nat
  |(0 :: t₁) t₂ n acc := simp_comp_aux t₁ t₂ 0 (n::acc)
  |((succ h₁) :: t₁) (h₂::t₂) n acc :=
     have h₁ < (succ h₁), begin apply lt_succ_of_le, apply le_of_eq, refl end,
     simp_comp_aux (h₁::t₁) t₂ (n+h₂) acc
  |_ _ n acc := acc

  #eval simp_comp_aux [3,0,1] [2,2,1,2] 0 []

  instance simplex_has_id_comp : has_id_comp simplex :=
  { hom := λ n m, {l : list nat // l.length = n ∧ list.foldr (+) 0 l = m}
  , id := λ n, ⟨list.repeat 1 n, begin split, simp, induction n, simp, simp [n_ih] end⟩
  , comp := λ i j k f g, ⟨list.reverse $ simp_comp_aux f g 0 [], begin cases f with f fp, cases g with g gp, simp, sorry  end⟩
  }

  instance simplex_is_category : category (simplex) := {!!}

  -- inductive shom : simplex → simplex → Type
  -- |d {n} : (fin (succ (succ n))) → shom (succ n) n -- face maps
  -- |s {n} : (fin (succ n)) → shom n (succ n) -- degeneracy maps.
  -- |i {n} : shom n n
  -- |c {i j k} : shom i j → shom j k → shom i k
  -- open shom

  -- inductive srel {n m : simplex} : shom n m → shom n m → Prop
  -- |r1 {n i j}: i < j → srel (c (d i) (d (succ j))) (c (d j) (d i))
  -- |r2 {n i j} : i ≤ j → srel (c (s i) (s j)) (c (s (succ j)) (s i))
  -- |r3 {n i j} : i < j → srel (c (d (i) (succ j))) (c (s j) (d i))
  -- |r4 {n i j} : i = j → srel (c (d (i) (j))) shom.i
  -- |r5 {n i j} : i = j + 1 : srel (c (d (succ i) (j))) (c (s j) (d i))

end simplicial

/- [TODO] ideas for wasting time.

Sieves and Grotendiek Topologies

 -/

end cat


macro "lemma" n:declId sig:declSig val:declVal : command => `(theorem $n $sig $val)

lemma fooSimple (n : Nat) : Prop :=
  if n = 0 then True else False

lemma fooPat : Nat → Prop
  | 0   => True
  | n+1 => False


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

! This file was ported from Lean 3 source module data.char
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/

/-!
# More `char` instances

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

This file provides a `linear_order` instance on `char`. `char` is the type of Unicode scalar values.
-/

instance : LinearOrder Char :=
  { Char.hasLe, Char.hasLt with
    le_refl := fun a => @le_refl ℕ _ _
    le_trans := fun a b c => @le_trans ℕ _ _ _ _
    le_antisymm := fun a b h₁ h₂ => Char.eq_of_veq <| le_antisymm h₁ h₂
    le_total := fun a b => @le_total ℕ _ _ _
    lt_iff_le_not_le := fun a b => @lt_iff_le_not_le ℕ _ _ _
    decidableLe := Char.decidableLe
    DecidableEq := Char.decidableEq
    decidableLt := Char.decidableLt }

#print Char.ofNat_toNat /-
theorem Char.ofNat_toNat {c : Char} (h : IsValidChar c.toNat) : Char.ofNat c.toNat = c :=
  by
  rw [Char.ofNat, dif_pos h]
  cases c
  simp [Char.toNat]
#align char.of_nat_to_nat Char.ofNat_toNat
-/


/-
Copyright (c) 2018 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Kenny Lau, Johan Commelin, Mario Carneiro, Kevin Buzzard
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.group_theory.submonoid.basic
import Mathlib.algebra.big_operators.basic
import Mathlib.PostPort

universes u_2 l u_1

namespace Mathlib

/-!
# Submonoids

This file defines unbundled multiplicative and additive submonoids (deprecated). For bundled form
see `group_theory/submonoid`.

We some results about images and preimages of submonoids under monoid homomorphisms. These theorems
use unbundled monoid homomorphisms (also deprecated).

There are also theorems about the submonoids generated by an element or a subset of a monoid,
defined inductively.

## Implementation notes

Unbundled submonoids will slowly be removed from mathlib.

## Tags
submonoid, submonoids, is_submonoid
-/

/-- `s` is an additive submonoid: a set containing 0 and closed under addition. -/
class is_add_submonoid {A : Type u_2} [add_monoid A] (s : set A)
where
  zero_mem : 0 ∈ s
  add_mem : ∀ {a b : A}, a ∈ s → b ∈ s → a + b ∈ s

/-- `s` is a submonoid: a set containing 1 and closed under multiplication. -/
class is_submonoid {M : Type u_1} [monoid M] (s : set M)
where
  one_mem : 1 ∈ s
  mul_mem : ∀ {a b : M}, a ∈ s → b ∈ s → a * b ∈ s

theorem additive.is_add_submonoid {M : Type u_1} [monoid M] (s : set M) [is_submonoid s] : is_add_submonoid s :=
  is_submonoid.dcases_on _inst_3
    fun (one_mem : 1 ∈ s) (mul_mem : ∀ {a b : M}, a ∈ s → b ∈ s → a * b ∈ s) =>
      idRhs (is_add_submonoid s) (is_add_submonoid.mk one_mem mul_mem)

theorem additive.is_add_submonoid_iff {M : Type u_1} [monoid M] {s : set M} : is_add_submonoid s ↔ is_submonoid s := sorry

theorem multiplicative.is_submonoid {A : Type u_2} [add_monoid A] (s : set A) [is_add_submonoid s] : is_submonoid s :=
  is_add_submonoid.dcases_on _inst_3
    fun (zero_mem : 0 ∈ s) (add_mem : ∀ {a b : A}, a ∈ s → b ∈ s → a + b ∈ s) =>
      idRhs (is_submonoid s) (is_submonoid.mk zero_mem add_mem)

theorem multiplicative.is_submonoid_iff {A : Type u_2} [add_monoid A] {s : set A} : is_submonoid s ↔ is_add_submonoid s := sorry

/-- The intersection of two submonoids of a monoid `M` is a submonoid of `M`. -/
protected instance is_submonoid.inter {M : Type u_1} [monoid M] (s₁ : set M) (s₂ : set M) [is_submonoid s₁] [is_submonoid s₂] : is_submonoid (s₁ ∩ s₂) :=
  is_submonoid.mk { left := is_submonoid.one_mem, right := is_submonoid.one_mem }
    fun (x y : M) (hx : x ∈ s₁ ∩ s₂) (hy : y ∈ s₁ ∩ s₂) =>
      { left := is_submonoid.mul_mem (and.left hx) (and.left hy),
        right := is_submonoid.mul_mem (and.right hx) (and.right hy) }

/-- The intersection of an indexed set of submonoids of a monoid `M` is a submonoid of `M`. -/
protected instance is_add_submonoid.Inter {M : Type u_1} [add_monoid M] {ι : Sort u_2} (s : ι → set M) [h : ∀ (y : ι), is_add_submonoid (s y)] : is_add_submonoid (set.Inter s) :=
  is_add_submonoid.mk (iff.mpr set.mem_Inter fun (y : ι) => is_add_submonoid.zero_mem)
    fun (x₁ x₂ : M) (h₁ : x₁ ∈ set.Inter s) (h₂ : x₂ ∈ set.Inter s) =>
      iff.mpr set.mem_Inter
        fun (y : ι) => is_add_submonoid.add_mem (iff.mp set.mem_Inter h₁ y) (iff.mp set.mem_Inter h₂ y)

/-- The union of an indexed, directed, nonempty set of submonoids of a monoid `M` is a submonoid
    of `M`. -/
theorem is_add_submonoid_Union_of_directed {M : Type u_1} [add_monoid M] {ι : Type u_2} [hι : Nonempty ι] (s : ι → set M) [∀ (i : ι), is_add_submonoid (s i)] (directed : ∀ (i j : ι), ∃ (k : ι), s i ⊆ s k ∧ s j ⊆ s k) : is_add_submonoid (set.Union fun (i : ι) => s i) := sorry

/-- The set of natural number powers `1, x, x², ...` of an element `x` of a monoid. -/
/-- The set of natural number multiples `0, x, 2x, ...` of an element `x` of an `add_monoid`. -/
def powers {M : Type u_1} [monoid M] (x : M) : set M :=
  set_of fun (y : M) => ∃ (n : ℕ), x ^ n = y

def multiples {A : Type u_2} [add_monoid A] (x : A) : set A :=
  set_of fun (y : A) => ∃ (n : ℕ), n •ℕ x = y

/-- 1 is in the set of natural number powers of an element of a monoid. -/
theorem powers.one_mem {M : Type u_1} [monoid M] {x : M} : 1 ∈ powers x :=
  Exists.intro 0 (pow_zero x)

/-- 0 is in the set of natural number multiples of an element of an `add_monoid`. -/
theorem multiples.zero_mem {A : Type u_2} [add_monoid A] {x : A} : 0 ∈ multiples x :=
  Exists.intro 0 (zero_nsmul x)

/-- An element of a monoid is in the set of that element's natural number powers. -/
theorem powers.self_mem {M : Type u_1} [monoid M] {x : M} : x ∈ powers x :=
  Exists.intro 1 (pow_one x)

/-- An element of an `add_monoid` is in the set of that element's natural number multiples. -/
theorem multiples.self_mem {A : Type u_2} [add_monoid A] {x : A} : x ∈ multiples x :=
  Exists.intro 1 (one_nsmul x)

/-- The set of natural number powers of an element of a monoid is closed under multiplication. -/
theorem powers.mul_mem {M : Type u_1} [monoid M] {x : M} {y : M} {z : M} : y ∈ powers x → z ∈ powers x → y * z ∈ powers x := sorry

/-- The set of natural number multiples of an element of an `add_monoid` is closed under
    addition. -/
theorem multiples.add_mem {A : Type u_2} [add_monoid A] {x : A} {y : A} {z : A} : y ∈ multiples x → z ∈ multiples x → y + z ∈ multiples x :=
  powers.mul_mem

/-- The set of natural number powers of an element of a monoid `M` is a submonoid of `M`. -/
protected instance multiples.is_add_submonoid {M : Type u_1} [add_monoid M] (x : M) : is_add_submonoid (multiples x) :=
  is_add_submonoid.mk multiples.zero_mem fun (y z : M) => multiples.add_mem

/-- A monoid is a submonoid of itself. -/
protected instance univ.is_submonoid {M : Type u_1} [monoid M] : is_submonoid set.univ :=
  is_submonoid.mk (eq.mpr (id (propext ((fun {α : Type u_1} (x : α) => iff_true_intro (set.mem_univ x)) 1))) trivial)
    (eq.mpr
      (id
        (Eq.trans
          (Eq.trans
            (forall_congr_eq
              fun (a : M) =>
                forall_congr_eq
                  fun (b : M) =>
                    Eq.trans
                      (imp_congr_eq (propext ((fun {α : Type u_1} (x : α) => iff_true_intro (set.mem_univ x)) a))
                        (Eq.trans
                          (imp_congr_eq (propext ((fun {α : Type u_1} (x : α) => iff_true_intro (set.mem_univ x)) b))
                            (propext ((fun {α : Type u_1} (x : α) => iff_true_intro (set.mem_univ x)) (a * b))))
                          (propext (forall_prop_of_true True.intro))))
                      (propext (forall_prop_of_true True.intro)))
            (propext (forall_const M)))
          (propext (forall_const M))))
      trivial)

/-- The preimage of a submonoid under a monoid hom is a submonoid of the domain. -/
protected instance preimage.is_add_submonoid {M : Type u_1} [add_monoid M] {N : Type u_2} [add_monoid N] (f : M → N) [is_add_monoid_hom f] (s : set N) [is_add_submonoid s] : is_add_submonoid (f ⁻¹' s) :=
  is_add_submonoid.mk
    ((fun (this : f 0 ∈ s) => this)
      (eq.mpr (id (Eq._oldrec (Eq.refl (f 0 ∈ s)) (is_add_monoid_hom.map_zero f))) is_add_submonoid.zero_mem))
    fun (a b : M) (ha : f a ∈ s) (hb : f b ∈ s) =>
      (fun (this : f (a + b) ∈ s) => this)
        (eq.mpr (id (Eq._oldrec (Eq.refl (f (a + b) ∈ s)) (is_add_monoid_hom.map_add f a b)))
          (is_add_submonoid.add_mem ha hb))

/-- The image of a submonoid under a monoid hom is a submonoid of the codomain. -/
instance image.is_add_submonoid {M : Type u_1} [add_monoid M] {γ : Type u_2} [add_monoid γ] (f : M → γ) [is_add_monoid_hom f] (s : set M) [is_add_submonoid s] : is_add_submonoid (f '' s) := sorry

/-- The image of a monoid hom is a submonoid of the codomain. -/
protected instance range.is_add_submonoid {M : Type u_1} [add_monoid M] {γ : Type u_2} [add_monoid γ] (f : M → γ) [is_add_monoid_hom f] : is_add_submonoid (set.range f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (is_add_submonoid (set.range f))) (Eq.symm set.image_univ)))
    (image.is_add_submonoid f set.univ)

/-- Submonoids are closed under natural powers. -/
theorem is_submonoid.pow_mem {M : Type u_1} [monoid M] {s : set M} {a : M} [is_submonoid s] (h : a ∈ s) {n : ℕ} : a ^ n ∈ s := sorry

/-- An `add_submonoid` is closed under multiplication by naturals. -/
theorem is_add_submonoid.smul_mem {A : Type u_2} [add_monoid A] {t : set A} {a : A} [is_add_submonoid t] (h : a ∈ t) {n : ℕ} : n •ℕ a ∈ t :=
  is_submonoid.pow_mem

/-- The set of natural number powers of an element of a submonoid is a subset of the submonoid. -/
theorem is_submonoid.power_subset {M : Type u_1} [monoid M] {s : set M} {a : M} [is_submonoid s] (h : a ∈ s) : powers a ⊆ s := sorry

/-- The set of natural number multiples of an element of an `add_submonoid` is a subset of the
    `add_submonoid`. -/
theorem is_add_submonoid.multiple_subset {A : Type u_2} [add_monoid A] {t : set A} {a : A} [is_add_submonoid t] : a ∈ t → multiples a ⊆ t :=
  is_submonoid.power_subset

namespace is_submonoid

/-- The product of a list of elements of a submonoid is an element of the submonoid. -/
theorem list_prod_mem {M : Type u_1} [monoid M] {s : set M} [is_submonoid s] {l : List M} : (∀ (x : M), x ∈ l → x ∈ s) → list.prod l ∈ s := sorry

/-- The product of a multiset of elements of a submonoid of a `comm_monoid` is an element of
the submonoid. -/
theorem Mathlib.is_add_submonoid.multiset_sum_mem {M : Type u_1} [add_comm_monoid M] (s : set M) [is_add_submonoid s] (m : multiset M) : (∀ (a : M), a ∈ m → a ∈ s) → multiset.sum m ∈ s := sorry

/-- The product of elements of a submonoid of a `comm_monoid` indexed by a `finset` is an element
of the submonoid. -/
theorem finset_prod_mem {M : Type u_1} {A : Type u_2} [comm_monoid M] (s : set M) [is_submonoid s] (f : A → M) (t : finset A) : (∀ (b : A), b ∈ t → f b ∈ s) → (finset.prod t fun (b : A) => f b) ∈ s := sorry

end is_submonoid

-- TODO: modify `subtype_instance` to produce this definition, then use it here

--  and for `subtype.group`

/-- Submonoids are themselves monoids. -/
def subtype.add_monoid {M : Type u_1} [add_monoid M] {s : set M} [is_add_submonoid s] : add_monoid ↥s :=
  add_monoid.mk (fun (x y : ↥s) => { val := ↑x + ↑y, property := sorry }) sorry
    { val := 0, property := is_add_submonoid.zero_mem } sorry sorry

/-- Submonoids of commutative monoids are themselves commutative monoids. -/
def subtype.add_comm_monoid {M : Type u_1} [add_comm_monoid M] {s : set M} [is_add_submonoid s] : add_comm_monoid ↥s :=
  add_comm_monoid.mk add_monoid.add sorry add_monoid.zero sorry sorry sorry

/-- Submonoids inherit the 1 of the monoid. -/
@[simp] theorem is_submonoid.coe_one {M : Type u_1} [monoid M] {s : set M} [is_submonoid s] : ↑1 = 1 :=
  rfl

/-- Submonoids inherit the multiplication of the monoid. -/
@[simp] theorem is_add_submonoid.coe_add {M : Type u_1} [add_monoid M] {s : set M} [is_add_submonoid s] (a : ↥s) (b : ↥s) : ↑(a + b) = ↑a + ↑b :=
  rfl

/-- Submonoids inherit the exponentiation by naturals of the monoid. -/
@[simp] theorem is_submonoid.coe_pow {M : Type u_1} [monoid M] {s : set M} [is_submonoid s] (a : ↥s) (n : ℕ) : ↑(a ^ n) = ↑a ^ n := sorry

/-- An `add_submonoid` inherits the multiplication by naturals of the `add_monoid`. -/
@[simp] theorem is_add_submonoid.smul_coe {A : Type u_1} [add_monoid A] {s : set A} [is_add_submonoid s] (a : ↥s) (n : ℕ) : ↑(n •ℕ a) = n •ℕ ↑a := sorry

/-- The natural injection from a submonoid into the monoid is a monoid hom. -/
protected instance subtype_val.is_add_monoid_hom {M : Type u_1} [add_monoid M] {s : set M} [is_add_submonoid s] : is_add_monoid_hom subtype.val :=
  is_add_monoid_hom.mk rfl

/-- The natural injection from a submonoid into the monoid is a monoid hom. -/
protected instance coe.is_add_monoid_hom {M : Type u_1} [add_monoid M] {s : set M} [is_add_submonoid s] : is_add_monoid_hom coe :=
  subtype_val.is_add_monoid_hom

/-- Given a monoid hom `f : γ → M` whose image is contained in a submonoid `s`, the induced map
    from `γ` to `s` is a monoid hom. -/
protected instance subtype_mk.is_add_monoid_hom {M : Type u_1} [add_monoid M] {s : set M} {γ : Type u_2} [add_monoid γ] [is_add_submonoid s] (f : γ → M) [is_add_monoid_hom f] (h : ∀ (x : γ), f x ∈ s) : is_add_monoid_hom fun (x : γ) => { val := f x, property := h x } :=
  is_add_monoid_hom.mk (subtype.eq (is_add_monoid_hom.map_zero f))

/-- Given two submonoids `s` and `t` such that `s ⊆ t`, the natural injection from `s` into `t` is
    a monoid hom. -/
protected instance set_inclusion.is_monoid_hom {M : Type u_1} [monoid M] {s : set M} (t : set M) [is_submonoid s] [is_submonoid t] (h : s ⊆ t) : is_monoid_hom (set.inclusion h) :=
  subtype_mk.is_monoid_hom (fun (x : ↥s) => ↑x) fun (x : ↥s) => set.inclusion._proof_1 h x

namespace add_monoid

/-- The inductively defined membership predicate for the submonoid generated by a subset of a
    monoid. -/
inductive in_closure {A : Type u_2} [add_monoid A] (s : set A) : A → Prop
where
| basic : ∀ {a : A}, a ∈ s → in_closure s a
| zero : in_closure s 0
| add : ∀ {a b : A}, in_closure s a → in_closure s b → in_closure s (a + b)

end add_monoid

namespace monoid

/-- The inductively defined membership predicate for the `add_submonoid` generated by a subset of an
    add_monoid. -/
inductive in_closure {M : Type u_1} [monoid M] (s : set M) : M → Prop
where
| basic : ∀ {a : M}, a ∈ s → in_closure s a
| one : in_closure s 1
| mul : ∀ {a b : M}, in_closure s a → in_closure s b → in_closure s (a * b)

/-- The inductively defined submonoid generated by a subset of a monoid. -/
def Mathlib.add_monoid.closure {M : Type u_1} [add_monoid M] (s : set M) : set M :=
  set_of fun (a : M) => add_monoid.in_closure s a

protected instance Mathlib.add_monoid.closure.is_add_submonoid {M : Type u_1} [add_monoid M] (s : set M) : is_add_submonoid (add_monoid.closure s) :=
  is_add_submonoid.mk add_monoid.in_closure.zero fun (a b : M) => add_monoid.in_closure.add

/-- A subset of a monoid is contained in the submonoid it generates. -/
theorem Mathlib.add_monoid.subset_closure {M : Type u_1} [add_monoid M] {s : set M} : s ⊆ add_monoid.closure s :=
  fun (a : M) => add_monoid.in_closure.basic

/-- The submonoid generated by a set is contained in any submonoid that contains the set. -/
theorem Mathlib.add_monoid.closure_subset {M : Type u_1} [add_monoid M] {s : set M} {t : set M} [is_add_submonoid t] (h : s ⊆ t) : add_monoid.closure s ⊆ t := sorry

/-- Given subsets `t` and `s` of a monoid `M`, if `s ⊆ t`, the submonoid of `M` generated by `s` is
    contained in the submonoid generated by `t`. -/
theorem Mathlib.add_monoid.closure_mono {M : Type u_1} [add_monoid M] {s : set M} {t : set M} (h : s ⊆ t) : add_monoid.closure s ⊆ add_monoid.closure t :=
  add_monoid.closure_subset (set.subset.trans h add_monoid.subset_closure)

/-- The submonoid generated by an element of a monoid equals the set of natural number powers of
    the element. -/
theorem closure_singleton {M : Type u_1} [monoid M] {x : M} : closure (singleton x) = powers x :=
  set.eq_of_subset_of_subset (closure_subset (iff.mpr set.singleton_subset_iff powers.self_mem))
    (is_submonoid.power_subset (iff.mp set.singleton_subset_iff subset_closure))

/-- The image under a monoid hom of the submonoid generated by a set equals the submonoid generated
    by the image of the set under the monoid hom. -/
theorem Mathlib.add_monoid.image_closure {M : Type u_1} [add_monoid M] {A : Type u_2} [add_monoid A] (f : M → A) [is_add_monoid_hom f] (s : set M) : f '' add_monoid.closure s = add_monoid.closure (f '' s) := sorry

/-- Given an element `a` of the submonoid of a monoid `M` generated by a set `s`, there exists
a list of elements of `s` whose product is `a`. -/
theorem Mathlib.add_monoid.exists_list_of_mem_closure {M : Type u_1} [add_monoid M] {s : set M} {a : M} (h : a ∈ add_monoid.closure s) : ∃ (l : List M), (∀ (x : M), x ∈ l → x ∈ s) ∧ list.sum l = a := sorry

/-- Given sets `s, t` of a commutative monoid `M`, `x ∈ M` is in the submonoid of `M` generated by
    `s ∪ t` iff there exists an element of the submonoid generated by `s` and an element of the
    submonoid generated by `t` whose product is `x`. -/
theorem Mathlib.add_monoid.mem_closure_union_iff {M : Type u_1} [add_comm_monoid M] {s : set M} {t : set M} {x : M} : x ∈ add_monoid.closure (s ∪ t) ↔
  ∃ (y : M), ∃ (H : y ∈ add_monoid.closure s), ∃ (z : M), ∃ (H : z ∈ add_monoid.closure t), y + z = x := sorry

end monoid

/-- Create a bundled submonoid from a set `s` and `[is_submonoid s]`. -/
def add_submonoid.of {M : Type u_1} [add_monoid M] (s : set M) [h : is_add_submonoid s] : add_submonoid M :=
  add_submonoid.mk s is_add_submonoid.zero_mem is_add_submonoid.add_mem

protected instance submonoid.is_submonoid {M : Type u_1} [monoid M] (S : submonoid M) : is_submonoid ↑S :=
  is_submonoid.mk (submonoid.one_mem' S) (submonoid.mul_mem' S)