import tactic

-- TODO: Define pq for union, which is coproduct (?). Also universal property.

universes u v w

class has_rhd (α : Type u) := (rhd : α → α → α)
class has_lhd (α : Type u) := (lhd : α → α → α)

reserve infixr `▷` :70
reserve infixr `◁` :70

infixr ▷ := has_rhd.rhd
infixr ◁ := has_lhd.lhd

/-
class rack (R : Type u) extends has_lhd R, has_rhd R :=
(rhd_dist : ∀ a b c : R, a ▷ (b ▷ c) = (a ▷ b) ▷ (a ▷ c))
(lhd_dist : ∀ a b c : R, (c ◁ b) ◁ a = (c ◁ a) ◁ (b ◁ a))
(lhd_rhd : ∀ a b : R, (a ▷ b) ◁ a = b)
(rhd_lhd : ∀ a b : R, a ▷ (b ◁ a) = b)

class quandle (Q : Type u) extends rack Q :=
(rhd_idem : ∀ a : Q, a ▷ a = a)
(lhd_idem : ∀ a : Q, a ◁ a = a)
-/

class power_quandle (Q : Type u) extends has_rhd Q, has_pow Q int, has_one Q :=
(rhd_dist : ∀ a b c : Q, a ▷ (b ▷ c) = (a ▷ b) ▷ (a ▷ c))
(rhd_idem : ∀ a : Q, a ▷ a = a)
(pow_one : ∀ a : Q, pow a 1 = a)
(pow_zero : ∀ a : Q, pow a 0 = 1 )
(pow_comp : ∀ a : Q, ∀ n m : int, (a^n)^m = a^(n * m))
(rhd_one : ∀ a : Q, a ▷ 1 = 1)
(one_rhd : ∀ a : Q, 1 ▷ a = a)
(pow_rhd : ∀ a b : Q, ∀ n : int, (a ▷ b)^n = a ▷ (b^n))
(rhd_pow_add : ∀ a b : Q, ∀ n m : int, (pow a n) ▷ ((pow a m) ▷ b) = (a^(n + m) ▷ b))

section power_quandle

variables {Q : Type u} [power_quandle Q]

instance pq_has_lhd : has_lhd Q := ⟨λ x y, y ^ (-1 : ℤ) ▷ x⟩

lemma lhd_rhd_pow : ∀ a b : Q, a ◁ b = b ^ (-1 : ℤ) ▷ a :=
begin
    intros a b,
    refl,
end

lemma lhd_dist : ∀ a b c : Q, (c ◁ b) ◁ a = (c ◁ a) ◁ (b ◁ a) :=
begin
    intros a b c,
    repeat {rw lhd_rhd_pow},
    rw power_quandle.pow_rhd,
    rw power_quandle.rhd_dist,
end

lemma lhd_rhd : ∀ a b : Q, (a ▷ b) ◁ a = b :=
begin
    intros a b,
    rw lhd_rhd_pow,
    rw ←power_quandle.pow_one a,
    rw power_quandle.pow_comp,
    rw power_quandle.rhd_pow_add,
    simp only [mul_one, mul_neg_eq_neg_mul_symm, add_left_neg],
    rw power_quandle.pow_zero,
    rw power_quandle.one_rhd,
end

lemma rhd_lhd : ∀ a b : Q, a ▷ (b ◁ a) = b :=
begin
    intros a b,
    rw lhd_rhd_pow,
    rw ←power_quandle.pow_one a,
    rw power_quandle.pow_comp,
    rw power_quandle.rhd_pow_add,
    simp only [mul_one, mul_neg_eq_neg_mul_symm, add_right_neg],
    rw power_quandle.pow_zero,
    rw power_quandle.one_rhd,
end

lemma lhd_idem : ∀ a : Q, a ◁ a = a :=
begin
    intros a,
    conv {
        to_rhs,
        rw ←lhd_rhd a a,
    },
    apply congr_arg (λ b, b ◁ a),
    rw power_quandle.rhd_idem,
end

lemma lhd_pow : ∀ a b : Q, ∀ n : int, (b ◁ a)^n = (b^n) ◁ a :=
begin
    intros a b n,
    rw lhd_rhd_pow,
    rw power_quandle.pow_rhd,
    rw lhd_rhd_pow,
end

lemma rhd_lhd_pow : ∀ a b : Q, a ▷ b = b ◁ (a ^ (-1 : int)) :=
begin
    intros a b,
    rw lhd_rhd_pow,
    rw power_quandle.pow_comp,
    simp,
    rw power_quandle.pow_one,
end

lemma lhd_pow_add : ∀ a b : Q, ∀ n m : int, (b ◁ (a ^ m)) ◁ (a ^ n) = (b ◁ a^(n + m)) :=
begin
    intros a b n m,
    repeat {rw lhd_rhd_pow},
    repeat {rw power_quandle.pow_comp},
    rw power_quandle.rhd_pow_add,
    simp,
    rw int.add_comm,
end

lemma pq_one_pow : ∀ n : int, (1 : Q) ^ n = 1 :=
begin
    intro n,
    rw ←power_quandle.pow_zero (1 : Q),
    rw power_quandle.pow_comp,
    simp only [zero_mul],
end

lemma lhd_one : ∀ a : Q, a ◁ 1 = a :=
begin
    intro a,
    rw lhd_rhd_pow,
    rw pq_one_pow,
    rw power_quandle.one_rhd,
end

lemma one_lhd : ∀ a : Q, 1 ◁ a = 1 :=
begin
    intro a,
    rw lhd_rhd_pow,
    rw power_quandle.rhd_one,
end

lemma a_rhd_an : ∀ a : Q, ∀ n : int, a ▷ a ^ n = a ^ n :=
begin
    intros a n,
    rw ←power_quandle.pow_rhd,
    rw power_quandle.rhd_idem,
end

lemma pow_neg_one_rhd_self : ∀ a : Q , a ^ (-1 : ℤ) ▷ a = a :=
begin
    intro a,
    rw ←lhd_rhd_pow,
    exact lhd_idem a,
end

lemma a_inv_rhd_an : ∀ a : Q, ∀ n : int, a ^ (-1 : int) ▷ a ^ n = a ^ n :=
begin
    intros a n,
    rw ←power_quandle.pow_rhd,
    rw pow_neg_one_rhd_self,
end

lemma an_rhd_am_nat : ∀ a : Q, ∀ n : nat, ∀ m : int, a ^ (n : int) ▷ a ^ m = a ^ m :=
begin
    intros a n m,
    induction n with l hl,
    {
        have int_zero : ↑0 = (0 : int),
        {
            refl,
        },
        rw int_zero,
        rw power_quandle.pow_zero,
        rw power_quandle.one_rhd,
    },
    {
        rw nat.succ_eq_add_one,
        rw ←int.of_nat_eq_coe,
        rw int.of_nat_add l 1,
        rw ←power_quandle.rhd_pow_add,
        have int_one : int.of_nat 1 = (1 : int),
        {
            refl,
        },
        rw int_one,
        rw power_quandle.pow_one,
        rw a_rhd_an,
        assumption,
    },
end

lemma an_rhd_am : ∀ a : Q, ∀ n m : int, a ^ n ▷ a ^ m = a ^ m :=
begin
    intros a n m,
    induction n with n1 n2,
    {
        exact (an_rhd_am_nat a n1 m),
    },
    {
        induction n2 with l hl,
        {
            exact a_inv_rhd_an a m,
        },
        {
            rw int.neg_succ_of_nat_coe,
            rw ←int.of_nat_eq_coe,
            rw int.of_nat_add,
            simp,
            rw int.add_comm (-1) (-1 + -↑l),
            rw ←power_quandle.rhd_pow_add,
            rw a_inv_rhd_an a m,
            rw int.neg_succ_of_nat_coe at hl,
            simp at hl,
            assumption,
        },
    },
end

end power_quandle

section power_quandle_morphism

variables {Q1 : Type u} [power_quandle Q1] {Q2 : Type v} [power_quandle Q2] {Q3 : Type w} [power_quandle Q3]

def is_pq_morphism (f : Q1 → Q2) : Prop :=
(∀ a b : Q1, f(a ▷ b) = f(a) ▷ f(b)) ∧
(∀ a : Q1, ∀ n : int, f(a ^ n) = f(a) ^ n)

lemma lhd_preserved_by_morphism (f : Q1 → Q2) (hf : is_pq_morphism f) : ∀ a b : Q1, f(a ◁ b) = f(a) ◁ f(b) :=
begin
    intros a b,
    rw lhd_rhd_pow,
    cases hf with hf1 hf2,
    rw hf1,
    rw hf2,
    rw ←lhd_rhd_pow,
end

lemma rhd_preserved_by_morphism (f : Q1 → Q2) (hf : is_pq_morphism f) : ∀ a b : Q1, f(a ▷ b) = f(a) ▷ f(b) :=
begin
    intros a b,
    cases hf with hf1 hf2,
    rw hf1,
end

lemma pow_preserved_by_morphism (f : Q1 → Q2) (hf : is_pq_morphism f) : ∀ a : Q1, ∀ n : ℤ, f(a ^ n) = (f a) ^ n :=
begin
    intros a n,
    cases hf with hf1 hf2,
    rw hf2,
end

lemma one_preserved_by_morphism (f : Q1 → Q2) (hf : is_pq_morphism f) : f 1 = 1 :=
begin
    rw ←power_quandle.pow_zero (1 : Q1),
    cases hf with hf1 hf2,
    rw hf2,
    rw power_quandle.pow_zero,
end

lemma id_is_pq_morphism : is_pq_morphism (id : Q1 → Q1) :=
begin
    split,
    {
        intros a b,
        simp,
    },
    {
        intros a n,
        simp,
    },
end

lemma pq_morphism_comp (f : Q1 → Q2) (g : Q2 → Q3) (hf : is_pq_morphism f) (hg : is_pq_morphism g) : is_pq_morphism (g ∘ f) :=
begin
    cases hf with hf1 hf2,
    cases hg with hg1 hg2,
    split,
    {
        intros a b,
        simp,
        rw hf1,
        rw hg1,
    },
    {
        intros a n,
        simp,
        rw hf2,
        rw hg2,
    },
end

variables {Q4 : Type u} [power_quandle Q4]

-- This is ever-so-slightly pointless but with this we are completely sure that we are dealing with a category.
lemma pq_morphism_assoc (f : Q1 → Q2) (g : Q2 → Q3) (h : Q3 → Q4) (hf : is_pq_morphism f) (hg : is_pq_morphism g) (hh : is_pq_morphism h) : h ∘ (g ∘ f) = (h ∘ g) ∘ f :=
begin
    refl,
end

lemma pq_iso_inv_is_pq_morphism (f : Q1 ≃ Q2) (hf : is_pq_morphism f) : is_pq_morphism (f.symm) :=
begin
  split,
  {
      intros a b,
      rw ←f.apply_symm_apply a,
      rw ←f.apply_symm_apply b,
      rw f.symm_apply_apply,
      rw f.symm_apply_apply,
      rw ←hf.1,
      rw f.symm_apply_apply,
  },
  {
      intros a n,
      rw ←f.apply_symm_apply a,
      rw ←hf.2,
      rw f.symm_apply_apply,
      rw f.symm_apply_apply,
  },
end

end power_quandle_morphism

section power_quandle_rhd_map

variables {Q : Type u} [power_quandle Q]

def rhd_map (x : Q) : Q → Q := λ y, x ▷ y

lemma rhd_map_def (x : Q) (y : Q) : rhd_map x y = x ▷ y := rfl

lemma rhd_map_is_pq_morphism (x : Q) : is_pq_morphism (rhd_map x) :=
begin
  split,
  {
    intros a b,
    simp only [rhd_map_def],
    apply power_quandle.rhd_dist,
  },
  {
    intros a n,
    simp only [rhd_map_def],
    apply eq.symm,
    apply power_quandle.pow_rhd,
  },
end

lemma rhd_map_is_injective (x : Q) : function.injective (rhd_map x) :=
begin
  intros a b hab,
  have hab1 := congr_arg (λ y, y ◁ x) hab,
  simp only [rhd_map_def, lhd_rhd] at hab1,
  exact hab1,
end

lemma rhd_map_is_surjective (x : Q) : function.surjective (rhd_map x) :=
begin
  intros a,
  use (a ◁ x),
  simp only [rhd_map_def, rhd_lhd],
end

lemma rhd_map_is_bijective (x : Q) : function.bijective (rhd_map x) :=
begin
  split,
  apply rhd_map_is_injective,
  apply rhd_map_is_surjective,
end

end power_quandle_rhd_map

section power_quandle_product

variables {Q1 : Type u} [power_quandle Q1] {Q2 : Type v} [power_quandle Q2]

def rhd_product (x : Q1 × Q2) (y : Q1 × Q2) : (Q1 × Q2) := (x.1 ▷ y.1, x.2 ▷ y.2)

instance product_has_rhd : has_rhd (Q1 × Q2) := has_rhd.mk rhd_product

lemma rhd_def_prod (a b : Q1 × Q2) : a ▷ b = (a.1 ▷ b.1, a.2 ▷ b.2) := rfl

/-
lemma right_dist_product : ∀ a b c : Q1 × Q2, a ▷ (b ▷ c) = (a ▷ b) ▷ (a ▷ c) :=
begin
   intros a b c,
   repeat {rw rhd_def_prod},
   repeat {rw ←rack.rhd_dist},
end

lemma left_dist_product : ∀ a b c : Q1 × Q2, (c ◁ b) ◁ a = (c ◁ a) ◁ (b ◁ a) :=
begin
   intros a b c,
   repeat {rw lhd_def_prod},
   repeat {rw ←rack.lhd_dist},
end

lemma right_inv_product : ∀ a b : Q1 × Q2, (a ▷ b) ◁ a = b :=
begin
    intros a b,
    rw rhd_def_prod,
    rw lhd_def_prod,
    simp,
    repeat {rw rack.right_inv},
    simp,
end

lemma left_inv_product : ∀ a b : Q1 × Q2, a ▷ (b ◁ a) = b :=
begin
    intros a b,
    rw rhd_def_prod,
    rw lhd_def_prod,
    simp,
    repeat {rw rack.left_inv},
    simp,
end

instance product_rack : rack (Q1 × Q2) := rack.mk
(right_dist_product)
(left_dist_product)
(right_inv_product)
(left_inv_product)

lemma self_idem_right_prod : ∀ a : Q1 × Q2, a ▷ a = a :=
begin
    intro a,
    rw rhd_def_prod,
    repeat {rw quandle.self_idem_right},
    simp,
end

lemma self_idem_left_prod : ∀ a : Q1 × Q2, a ◁ a = a :=
begin
    intro a,
    rw lhd_def_prod,
    repeat {rw quandle.self_idem_left},
    simp,
end

instance product_quandle : quandle (Q1 × Q2) := quandle.mk
(self_idem_right_prod)
(self_idem_left_prod)

def pq_pow_prod (a : Q1 × Q2) (n : int) : (Q1 × Q2) := (a.1 ^ n, a.2 ^ n)

@[priority std.priority.default-1]
instance product_pq_has_pow : has_pow (Q1 × Q2) int := ⟨pq_pow_prod⟩

lemma pow_def_prod (a : Q1 × Q2) (n : int) : a ^ n = (a.1 ^ n, a.2 ^ n) := rfl

lemma pow_1_prod : ∀ a : Q1 × Q2, a ^ (1 : int) = a :=
begin
    intro a,
    rw pow_def_prod,
    repeat {rw power_quandle.pow_1},
    simp,
end

lemma pow_comp_prod : ∀ a : Q1 × Q2, ∀ n m : int, (a^n)^m = a^(n * m) :=
begin
    intros a n m,
    repeat {rw pow_def_prod},
    --simp,
    repeat {rw power_quandle.pow_comp},
end

lemma q_pow0_prod : ∀ a b : Q1 × Q2, a ▷ (b ^ (0 : int)) = (b ^ (0 : int)) :=
begin
    intros a b,
    repeat {rw pow_def_prod},
    rw rhd_def_prod,
    --simp,
    repeat {rw power_quandle.q_pow0},
end

lemma q_pown_right_prod : ∀ a b : Q1 × Q2, ∀ n : int, (a ▷ b)^n = a ▷ (b^n) :=
begin
    intros a b n,
    repeat {rw pow_def_prod},
    repeat {rw rhd_def_prod},
    --simp,
    repeat {rw power_quandle.q_pown_right},
end

lemma q_pown_left_prod : ∀ a b : Q1 × Q2, ∀ n : int, (b ◁ a)^n = (b^n) ◁ a :=
begin
    intros a b n,
    repeat {rw pow_def_prod},
    repeat {rw lhd_def_prod},
    --simp,
    repeat {rw q_pown_left},
end

lemma q_powneg_right_prod : ∀ a b : Q1 × Q2, a ▷ b = b ◁ (a ^ (-1 : int)) :=
begin
    intros a b,
    rw rhd_def_prod,
    rw lhd_def_prod,
    repeat {rw pow_def_prod},
    --simp,
    repeat {rw q_powneg_right},
end

lemma q_powneg_left_prod : ∀ a b : Q1 × Q2, b ◁ a = (a ^ (-1 : int)) ▷ b :=
begin
    intros a b,
    rw rhd_def_prod,
    rw lhd_def_prod,
    repeat {rw pow_def_prod},
    --simp,
    repeat {rw power_quandle.q_powneg_left},
end

lemma q_powadd_prod : ∀ a b : Q1 × Q2, ∀ n m : int, (pow a n) ▷ ((pow a m) ▷ b) = (a^(n + m) ▷ b) :=
begin
    intros a b n m,
    repeat {rw rhd_def_prod},
    repeat {rw pow_def_prod},
    --simp,
    repeat {rw power_quandle.q_powadd},
end

instance power_quandle_prod : power_quandle (Q1 × Q2) := power_quandle.mk
(pow_1_prod)
(pow_comp_prod)
(q_pow0_prod)
(q_pown_right_prod)
--(q_pown_left_prod)
--(q_powneg_right_prod)
(q_powneg_left_prod)
(q_powadd_prod)

-/

def pq_pow_prod (a : Q1 × Q2) (n : int) : (Q1 × Q2) := (a.1 ^ n, a.2 ^ n)

@[priority std.priority.default-1]
instance product_pq_has_pow : has_pow (Q1 × Q2) int := ⟨pq_pow_prod⟩

lemma pow_def_prod (a : Q1 × Q2) (n : int) : a ^ n = (a.1 ^ n, a.2 ^ n) := rfl

instance : power_quandle (Q1 × Q2) := {
  rhd_dist := begin
    intros a b c,
    simp only [rhd_def_prod, prod.mk.inj_iff],
    split;
    rw power_quandle.rhd_dist,
  end,
  rhd_idem := begin
    rintros ⟨a1, a2⟩,
    simp only [rhd_def_prod, prod.mk.inj_iff],
    split;
    rw power_quandle.rhd_idem,
  end,
  pow_one := begin
    rintros ⟨a1, a2⟩,
    simp only [pow_def_prod, prod.mk.inj_iff],
    split;
    rw power_quandle.pow_one,
  end,
  pow_zero := begin
    rintros ⟨a1, a2⟩,
    simp only [pow_def_prod, prod.mk_eq_one],
    split;
    rw power_quandle.pow_zero,
  end,
  pow_comp := begin
    rintros ⟨a1, a2⟩ n m,
    simp only [pow_def_prod, prod.mk.inj_iff],
    split;
    rw power_quandle.pow_comp,
  end,
  rhd_one := begin
    rintros ⟨a1, a2⟩,
    simp only [rhd_def_prod, prod.snd_one, prod.mk_eq_one, prod.fst_one],
    split;
    rw power_quandle.rhd_one,
  end,
  one_rhd :=  begin
    rintros ⟨a1, a2⟩,
    simp only [rhd_def_prod, prod.mk.inj_iff, prod.snd_one, prod.fst_one],
    split;
    rw power_quandle.one_rhd,
  end,
  pow_rhd := begin
    rintros ⟨a1, a2⟩ ⟨b1, b2⟩ n,
    simp only [rhd_def_prod, pow_def_prod, prod.mk.inj_iff],
    split;
    rw power_quandle.pow_rhd,
  end,
  rhd_pow_add := begin
    rintros ⟨a1, a2⟩ ⟨b1, b2⟩ n m,
    simp only [rhd_def_prod, pow_def_prod, prod.mk.inj_iff],
    split;
    rw power_quandle.rhd_pow_add,
  end,
  ..product_has_rhd,
  ..product_pq_has_pow,
  ..prod.has_one }

-- Universal property of product

def pi1 (a : Q1 × Q2) : Q1 := a.1
def pi2 (a : Q1 × Q2) : Q2 := a.2

lemma pi1_def (a : Q1 × Q2) : pi1 a = a.1 := rfl
lemma pi2_def (a : Q1 × Q2) : pi2 a = a.2 := rfl

lemma pi1_morph : is_pq_morphism (pi1 : (Q1 × Q2) → Q1) :=
begin
    split,
    {
        intros a b,
        repeat {rw pi1_def},
        rw rhd_def_prod,
    },
    {
        intros a n,
        repeat {rw pi1_def},
        rw pow_def_prod,
    },
end

lemma pi2_morph : is_pq_morphism (pi2 : (Q1 × Q2) → Q2) :=
begin
    split,
    {
        intros a b,
        repeat {rw pi2_def},
        rw rhd_def_prod,
    },
    {
        intros a n,
        repeat {rw pi2_def},
        rw pow_def_prod,
    },
end

variables {Y : Type u} [power_quandle Y]

theorem prod_universal_prop (f₁ : Y → Q1) (f₂ : Y → Q2) (hf₁ : is_pq_morphism f₁) (hf₂ : is_pq_morphism f₂) :
        ∃ f : (Y → Q1 × Q2), (is_pq_morphism f) ∧ (pi1 ∘ f = f₁) ∧ (pi2 ∘ f = f₂) :=
begin
    cases hf₁ with hf₁a hf₁b,
    cases hf₂ with hf₂a hf₂b,
    let f := λ a : Y, (f₁ a, f₂ a),
    have f_def : ∀ a, f(a) = (f₁ a, f₂ a),
    {
        intro a,
        refl,
    },
    existsi f,
    split,
    {
        split,
        {
            intros a b,
            repeat {rw f_def},
            rw rhd_def_prod,
            --simp,
            repeat {rw hf₁a},
            repeat {rw hf₂a},
        },
        {
            intros a n,
            repeat {rw f_def},
            rw pow_def_prod,
            --simp,
            repeat {rw hf₁b},
            repeat {rw hf₂b},
        },
    },
    split,
    {refl,},
    {refl,},
end

end power_quandle_product

section terminal_power_quandle

def rhd_terminal (x : unit) (y : unit) : unit := unit.star

instance terminal_has_rhd : has_rhd unit := has_rhd.mk rhd_terminal

lemma rhd_def_term (a b : unit) : a ▷ b = unit.star := rfl

lemma unit_eq : ∀ a b : unit, a = b :=
begin
    intros a b,
    induction a,
    induction b,
    refl,
end

/-
instance terminal_rack : rack unit := rack.mk
(begin intros, apply unit_eq end)
(begin intros, apply unit_eq end)
(begin intros, apply unit_eq end)
(begin intros, apply unit_eq end)

instance terminal_quandle : quandle unit := quandle.mk
(begin intros, apply unit_eq end)
(begin intros, apply unit_eq end)
-/

def terminal_pq_pow (a : unit) (n : int) : unit := unit.star

instance terminal_pow : has_pow unit int := has_pow.mk (terminal_pq_pow)

lemma pow_def_term (a : unit) (n : int) : a ^ n = unit.star := rfl

instance terminal_has_one : has_one unit := ⟨unit.star⟩

lemma terminal_one_def (a : unit) : (1 : unit) = a := begin
    apply unit_eq,
end

instance terminal_power_quandle : power_quandle unit := {
  rhd_dist := begin intros, apply unit_eq, end ,
  rhd_idem := begin intros, apply unit_eq, end,
  pow_one := begin intros, apply unit_eq, end,
  pow_zero := begin intros, apply unit_eq, end,
  pow_comp := begin intros, apply unit_eq, end,
  rhd_one := begin intros, apply unit_eq, end,
  one_rhd := begin intros, apply unit_eq, end,
  pow_rhd := begin intros, apply unit_eq, end,
  rhd_pow_add := begin intros, apply unit_eq, end,
  ..terminal_pow,
  ..terminal_has_rhd,
  ..terminal_has_one, }

-- Universal propery as terminal object

variables {Q : Type u} [power_quandle Q]

def terminal_morphism (a : Q) : unit := unit.star

lemma terminal_morphism_is_morphism : is_pq_morphism (terminal_morphism : (Q → unit)) :=
begin
    split,
    {
        intros a b,
        refl,
    },
    {
        intros a n,
        refl,
    },
end

theorem terminal_pq_is_terminal (f : Q → unit) (hf : is_pq_morphism f) : f = terminal_morphism :=
begin
    apply funext,
    intro a,
    apply unit_eq,
end

end terminal_power_quandle

section initial_power_quandle

-- Proof that it is intial in the category of power quandles

variables {Q : Type u} [power_quandle Q]

def initial_morphism (a : unit) : Q := 1

lemma initial_morphism_def (a : unit) : initial_morphism a = (1 : Q) := rfl

lemma initial_morphism_is_morphism : is_pq_morphism (initial_morphism : (unit → Q)) :=
begin
    split,
    {
        intros a b,
        simp only [initial_morphism_def, power_quandle.rhd_one],
    },
    {
        intros a n,
        simp only [initial_morphism_def, pq_one_pow],
    },
end

theorem initial_pq_is_initial (f : unit → Q) (hf : is_pq_morphism f) : f = initial_morphism :=
begin
    apply funext,
    intro a,
    induction a,
    rw initial_morphism_def,
    rw ←terminal_one_def punit.star,
    rw one_preserved_by_morphism f hf,
end

end initial_power_quandle


theorem ex (p q r : Prop) (h : p ∧ q ∧ r) : (¬q ∨ r) ∧ (¬r ∨ q) ∧ p := by
  simp [h]

#print ex


/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebraic_geometry.sheafed_space
import Mathlib.algebra.category.CommRing.limits
import Mathlib.algebra.category.CommRing.colimits
import Mathlib.algebraic_geometry.stalks
import Mathlib.ring_theory.ideal.basic
import Mathlib.PostPort

universes u_1 l u

namespace Mathlib

/-!
# The category of locally ringed spaces

We define (bundled) locally ringed spaces
(as `SheafedSpace CommRing` along with the fact that the stalks are local rings),
and morphisms between these (morphisms in `SheafedSpace` with `is_local_ring_hom` on the stalk maps).

## Future work
* Define the restriction along an open embedding
-/

namespace algebraic_geometry

/-- A `LocallyRingedSpace` is a topological space equipped with a sheaf of commutative rings
such that all the stalks are local rings.

A morphism of locally ringed spaces is a morphism of ringed spaces
such that the morphims induced on stalks are local ring homomorphisms. -/
structure LocallyRingedSpace
extends SheafedSpace CommRing
where
  local_ring : ∀ (x : ↥(PresheafedSpace.carrier (SheafedSpace.to_PresheafedSpace _to_SheafedSpace))),
  local_ring ↥(Top.presheaf.stalk (PresheafedSpace.presheaf (SheafedSpace.to_PresheafedSpace _to_SheafedSpace)) x)

namespace LocallyRingedSpace

/-- The underlying topological space of a locally ringed space. -/
def to_Top (X : LocallyRingedSpace) : Top :=
  PresheafedSpace.carrier (SheafedSpace.to_PresheafedSpace (to_SheafedSpace X))

protected instance has_coe_to_sort : has_coe_to_sort LocallyRingedSpace :=
  has_coe_to_sort.mk (Type u) fun (X : LocallyRingedSpace) => ↥(to_Top X)

-- PROJECT: how about a typeclass "has_structure_sheaf" to mediate the 𝒪 notation, rather

-- than defining it over and over for PresheafedSpace, LRS, Scheme, etc.

/-- The structure sheaf of a locally ringed space. -/
def 𝒪 (X : LocallyRingedSpace) : Top.sheaf CommRing (to_Top X) :=
  SheafedSpace.sheaf (to_SheafedSpace X)

/-- A morphism of locally ringed spaces is a morphism of ringed spaces
 such that the morphims induced on stalks are local ring homomorphisms. -/
def hom (X : LocallyRingedSpace) (Y : LocallyRingedSpace) :=
  Subtype
    fun (f : to_SheafedSpace X ⟶ to_SheafedSpace Y) =>
      ∀ (x : ↥(SheafedSpace.to_PresheafedSpace (to_SheafedSpace X))), is_local_ring_hom (PresheafedSpace.stalk_map f x)

protected instance category_theory.has_hom : category_theory.has_hom LocallyRingedSpace :=
  category_theory.has_hom.mk hom

theorem hom_ext {X : LocallyRingedSpace} {Y : LocallyRingedSpace} (f : hom X Y) (g : hom X Y) (w : subtype.val f = subtype.val g) : f = g :=
  subtype.eq w

/--
The stalk of a locally ringed space, just as a `CommRing`.
-/
-- TODO perhaps we should make a bundled `LocalRing` and return one here?

-- TODO define `sheaf.stalk` so we can write `X.𝒪.stalk` here?

def stalk (X : LocallyRingedSpace) (x : ↥X) : CommRing :=
  Top.presheaf.stalk (PresheafedSpace.presheaf (SheafedSpace.to_PresheafedSpace (to_SheafedSpace X))) x

/--
A morphism of locally ringed spaces `f : X ⟶ Y` induces
a local ring homomorphism from `Y.stalk (f x)` to `X.stalk x` for any `x : X`.
-/
def stalk_map {X : LocallyRingedSpace} {Y : LocallyRingedSpace} (f : X ⟶ Y) (x : ↥X) : stalk Y (coe_fn (PresheafedSpace.hom.base (subtype.val f)) x) ⟶ stalk X x :=
  PresheafedSpace.stalk_map (subtype.val f) x

protected instance stalk_map.is_local_ring_hom {X : LocallyRingedSpace} {Y : LocallyRingedSpace} (f : X ⟶ Y) (x : ↥X) : is_local_ring_hom (stalk_map f x) :=
  subtype.property f x

/-- The identity morphism on a locally ringed space. -/
def id (X : LocallyRingedSpace) : hom X X :=
  { val := 𝟙, property := sorry }

protected instance hom.inhabited (X : LocallyRingedSpace) : Inhabited (hom X X) :=
  { default := id X }

/-- Composition of morphisms of locally ringed spaces. -/
def comp {X : LocallyRingedSpace} {Y : LocallyRingedSpace} {Z : LocallyRingedSpace} (f : hom X Y) (g : hom Y Z) : hom X Z :=
  { val := subtype.val f ≫ subtype.val g, property := sorry }

/-- The category of locally ringed spaces. -/
protected instance category_theory.category : category_theory.category LocallyRingedSpace :=
  category_theory.category.mk

/-- The forgetful functor from `LocallyRingedSpace` to `SheafedSpace CommRing`. -/
def forget_to_SheafedSpace : LocallyRingedSpace ⥤ SheafedSpace CommRing :=
  category_theory.functor.mk (fun (X : LocallyRingedSpace) => to_SheafedSpace X)
    fun (X Y : LocallyRingedSpace) (f : X ⟶ Y) => subtype.val f

protected instance forget_to_SheafedSpace.category_theory.faithful : category_theory.faithful forget_to_SheafedSpace :=
  category_theory.faithful.mk

-- PROJECT: once we have `PresheafedSpace.restrict_stalk_iso`

-- (that restriction doesn't change stalks) we can uncomment this.

/-
def restrict {U : Top} (X : LocallyRingedSpace)
  (f : U ⟶ X.to_Top) (h : open_embedding f) : LocallyRingedSpace :=
{ local_ring :=
  begin
    intro x,
    dsimp at *,
    -- We show that the stalk of the restriction is isomorphic to the original stalk,
    have := X.to_SheafedSpace.to_PresheafedSpace.restrict_stalk_iso f h x,
    -- and then transfer `local_ring` across the ring equivalence.
    apply (this.CommRing_iso_to_ring_equiv).local_ring, -- import data.equiv.transfer_instance
    apply X.local_ring,
  end,
  .. X.to_SheafedSpace.restrict _ f h }
-/

/--
The global sections, notated Gamma.
-/
def Γ : LocallyRingedSpaceᵒᵖ ⥤ CommRing :=
  category_theory.functor.op forget_to_SheafedSpace ⋙ SheafedSpace.Γ

theorem Γ_def : Γ = category_theory.functor.op forget_to_SheafedSpace ⋙ SheafedSpace.Γ :=
  rfl

@[simp] theorem Γ_obj (X : LocallyRingedSpaceᵒᵖ) : category_theory.functor.obj Γ X =
  category_theory.functor.obj
    (PresheafedSpace.presheaf (SheafedSpace.to_PresheafedSpace (to_SheafedSpace (opposite.unop X)))) (opposite.op ⊤) :=
  rfl

theorem Γ_obj_op (X : LocallyRingedSpace) : category_theory.functor.obj Γ (opposite.op X) =
  category_theory.functor.obj (PresheafedSpace.presheaf (SheafedSpace.to_PresheafedSpace (to_SheafedSpace X)))
    (opposite.op ⊤) :=
  rfl

@[simp] theorem Γ_map {X : LocallyRingedSpaceᵒᵖ} {Y : LocallyRingedSpaceᵒᵖ} (f : X ⟶ Y) : category_theory.functor.map Γ f =
  category_theory.nat_trans.app (PresheafedSpace.hom.c (subtype.val (category_theory.has_hom.hom.unop f)))
      (opposite.op ⊤) ≫
    category_theory.functor.map
      (PresheafedSpace.presheaf (SheafedSpace.to_PresheafedSpace (to_SheafedSpace (opposite.unop Y))))
      (category_theory.has_hom.hom.op
        (topological_space.opens.le_map_top
          (PresheafedSpace.hom.base (subtype.val (category_theory.has_hom.hom.unop f))) ⊤)) :=
  rfl

theorem Γ_map_op {X : LocallyRingedSpace} {Y : LocallyRingedSpace} (f : X ⟶ Y) : category_theory.functor.map Γ (category_theory.has_hom.hom.op f) =
  category_theory.nat_trans.app (PresheafedSpace.hom.c (subtype.val f)) (opposite.op ⊤) ≫
    category_theory.functor.map (PresheafedSpace.presheaf (SheafedSpace.to_PresheafedSpace (to_SheafedSpace X)))
      (category_theory.has_hom.hom.op (topological_space.opens.le_map_top (PresheafedSpace.hom.base (subtype.val f)) ⊤)) :=
  rfl


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.big_operators.intervals
import Mathlib.topology.instances.real
import Mathlib.topology.algebra.module
import Mathlib.data.indicator_function
import Mathlib.data.equiv.encodable.lattice
import Mathlib.order.filter.at_top_bot
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4 u_5 u_6

namespace Mathlib

/-!
# Infinite sum over a topological monoid

This sum is known as unconditionally convergent, as it sums to the same value under all possible
permutations. For Euclidean spaces (finite dimensional Banach spaces) this is equivalent to absolute
convergence.

Note: There are summable sequences which are not unconditionally convergent! The other way holds
generally, see `has_sum.tendsto_sum_nat`.

## References

* Bourbaki: General Topology (1995), Chapter 3 §5 (Infinite sums in commutative groups)

-/

/-- Infinite sum on a topological monoid

The `at_top` filter on `finset β` is the limit of all finite sets towards the entire type. So we sum
up bigger and bigger sets. This sum operation is invariant under reordering. In particular,
the function `ℕ → ℝ` sending `n` to `(-1)^n / (n+1)` does not have a
sum for this definition, but a series which is absolutely convergent will have the correct sum.

This is based on Mario Carneiro's
[infinite sum `df-tsms` in Metamath](http://us.metamath.org/mpeuni/df-tsms.html).

For the definition or many statements, `α` does not need to be a topological monoid. We only add
this assumption later, for the lemmas where it is relevant.
-/
def has_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] (f : β → α)
    (a : α) :=
  filter.tendsto (fun (s : finset β) => finset.sum s fun (b : β) => f b) filter.at_top (nhds a)

/-- `summable f` means that `f` has some (infinite) sum. Use `tsum` to get the value. -/
def summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] (f : β → α) :=
  ∃ (a : α), has_sum f a

/-- `∑' i, f i` is the sum of `f` it exists, or 0 otherwise -/
def tsum {α : Type u_1} [add_comm_monoid α] [topological_space α] {β : Type u_2} (f : β → α) : α :=
  dite (summable f) (fun (h : summable f) => classical.some h) fun (h : ¬summable f) => 0

-- see Note [operator precedence of big operators]

theorem summable.has_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} (ha : summable f) : has_sum f (tsum fun (b : β) => f b) :=
  sorry

theorem has_sum.summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} {a : α} (h : has_sum f a) : summable f :=
  Exists.intro a h

/-- Constant zero function has sum `0` -/
theorem has_sum_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] :
    has_sum (fun (b : β) => 0) 0 :=
  sorry

theorem summable_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] :
    summable fun (b : β) => 0 :=
  has_sum.summable has_sum_zero

theorem tsum_eq_zero_of_not_summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] {f : β → α} (h : ¬summable f) : (tsum fun (b : β) => f b) = 0 :=
  sorry

theorem has_sum.has_sum_of_sum_eq {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] {f : β → α} {a : α} {g : γ → α}
    (h_eq :
      ∀ (u : finset γ),
        ∃ (v : finset β),
          ∀ (v' : finset β),
            v ⊆ v' →
              ∃ (u' : finset γ),
                u ⊆ u' ∧ (finset.sum u' fun (x : γ) => g x) = finset.sum v' fun (b : β) => f b)
    (hf : has_sum g a) : has_sum f a :=
  le_trans (filter.map_at_top_finset_sum_le_of_sum_eq h_eq) hf

theorem has_sum_iff_has_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] {f : β → α} {a : α} {g : γ → α}
    (h₁ :
      ∀ (u : finset γ),
        ∃ (v : finset β),
          ∀ (v' : finset β),
            v ⊆ v' →
              ∃ (u' : finset γ),
                u ⊆ u' ∧ (finset.sum u' fun (x : γ) => g x) = finset.sum v' fun (b : β) => f b)
    (h₂ :
      ∀ (v : finset β),
        ∃ (u : finset γ),
          ∀ (u' : finset γ),
            u ⊆ u' →
              ∃ (v' : finset β),
                v ⊆ v' ∧ (finset.sum v' fun (b : β) => f b) = finset.sum u' fun (x : γ) => g x) :
    has_sum f a ↔ has_sum g a :=
  { mp := has_sum.has_sum_of_sum_eq h₂, mpr := has_sum.has_sum_of_sum_eq h₁ }

theorem function.injective.has_sum_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {g : γ → β}
    (hg : function.injective g) (hf : ∀ (x : β), ¬x ∈ set.range g → f x = 0) :
    has_sum (f ∘ g) a ↔ has_sum f a :=
  sorry

theorem function.injective.summable_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [add_comm_monoid α] [topological_space α] {f : β → α} {g : γ → β} (hg : function.injective g)
    (hf : ∀ (x : β), ¬x ∈ set.range g → f x = 0) : summable (f ∘ g) ↔ summable f :=
  exists_congr fun (_x : α) => function.injective.has_sum_iff hg hf

theorem has_sum_subtype_iff_of_support_subset {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] {f : β → α} {a : α} {s : set β} (hf : function.support f ⊆ s) :
    has_sum (f ∘ coe) a ↔ has_sum f a :=
  sorry

theorem has_sum_subtype_iff_indicator {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] {f : β → α} {a : α} {s : set β} :
    has_sum (f ∘ coe) a ↔ has_sum (set.indicator s f) a :=
  sorry

@[simp] theorem has_sum_subtype_support {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] {f : β → α} {a : α} : has_sum (f ∘ coe) a ↔ has_sum f a :=
  has_sum_subtype_iff_of_support_subset (set.subset.refl (function.support f))

theorem has_sum_fintype {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [fintype β] (f : β → α) : has_sum f (finset.sum finset.univ fun (b : β) => f b) :=
  order_top.tendsto_at_top_nhds fun (s : finset β) => finset.sum s fun (b : β) => f b

protected theorem finset.has_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] (s : finset β) (f : β → α) :
    has_sum (f ∘ coe) (finset.sum s fun (b : β) => f b) :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (has_sum (f ∘ coe) (finset.sum s fun (b : β) => f b)))
        (Eq.symm finset.sum_attach)))
    (has_sum_fintype (f ∘ coe))

protected theorem finset.summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] (s : finset β) (f : β → α) : summable (f ∘ coe) :=
  has_sum.summable (finset.has_sum s f)

protected theorem set.finite.summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] {s : set β} (hs : set.finite s) (f : β → α) : summable (f ∘ coe) :=
  sorry

/-- If a function `f` vanishes outside of a finite set `s`, then it `has_sum` `∑ b in s, f b`. -/
theorem has_sum_sum_of_ne_finset_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] {f : β → α} {s : finset β} (hf : ∀ (b : β), ¬b ∈ s → f b = 0) :
    has_sum f (finset.sum s fun (b : β) => f b) :=
  iff.mp (has_sum_subtype_iff_of_support_subset (iff.mpr function.support_subset_iff' hf))
    (finset.has_sum s f)

theorem summable_of_ne_finset_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] {f : β → α} {s : finset β} (hf : ∀ (b : β), ¬b ∈ s → f b = 0) :
    summable f :=
  has_sum.summable (has_sum_sum_of_ne_finset_zero hf)

theorem has_sum_single {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} (b : β) (hf : ∀ (b' : β), b' ≠ b → f b' = 0) : has_sum f (f b) :=
  sorry

theorem has_sum_ite_eq {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    (b : β) (a : α) : has_sum (fun (b' : β) => ite (b' = b) a 0) a :=
  sorry

theorem equiv.has_sum_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] {f : β → α} {a : α} (e : γ ≃ β) : has_sum (f ∘ ⇑e) a ↔ has_sum f a :=
  sorry

theorem equiv.summable_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] {f : β → α} (e : γ ≃ β) : summable (f ∘ ⇑e) ↔ summable f :=
  exists_congr fun (a : α) => equiv.has_sum_iff e

theorem summable.prod_symm {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] {f : β × γ → α} (hf : summable f) :
    summable fun (p : γ × β) => f (prod.swap p) :=
  iff.mpr (equiv.summable_iff (equiv.prod_comm γ β)) hf

theorem equiv.has_sum_iff_of_support {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {g : γ → α}
    (e : ↥(function.support f) ≃ ↥(function.support g))
    (he : ∀ (x : ↥(function.support f)), g ↑(coe_fn e x) = f ↑x) : has_sum f a ↔ has_sum g a :=
  sorry

theorem has_sum_iff_has_sum_of_ne_zero_bij {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {g : γ → α}
    (i : ↥(function.support g) → β) (hi : ∀ {x y : ↥(function.support g)}, i x = i y → ↑x = ↑y)
    (hf : function.support f ⊆ set.range i) (hfg : ∀ (x : ↥(function.support g)), f (i x) = g ↑x) :
    has_sum f a ↔ has_sum g a :=
  sorry

theorem equiv.summable_iff_of_support {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [add_comm_monoid α] [topological_space α] {f : β → α} {g : γ → α}
    (e : ↥(function.support f) ≃ ↥(function.support g))
    (he : ∀ (x : ↥(function.support f)), g ↑(coe_fn e x) = f ↑x) : summable f ↔ summable g :=
  exists_congr fun (_x : α) => equiv.has_sum_iff_of_support e he

protected theorem has_sum.map {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] {f : β → α} {a : α} [add_comm_monoid γ] [topological_space γ]
    (hf : has_sum f a) (g : α →+ γ) (hg : continuous ⇑g) : has_sum (⇑g ∘ f) (coe_fn g a) :=
  sorry

protected theorem summable.map {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] {f : β → α} [add_comm_monoid γ] [topological_space γ] (hf : summable f)
    (g : α →+ γ) (hg : continuous ⇑g) : summable (⇑g ∘ f) :=
  has_sum.summable (has_sum.map (summable.has_sum hf) g hg)

/-- If `f : ℕ → α` has sum `a`, then the partial sums `∑_{i=0}^{n-1} f i` converge to `a`. -/
theorem has_sum.tendsto_sum_nat {α : Type u_1} [add_comm_monoid α] [topological_space α] {a : α}
    {f : ℕ → α} (h : has_sum f a) :
    filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top
        (nhds a) :=
  filter.tendsto.comp h filter.tendsto_finset_range

theorem has_sum.unique {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} {a₁ : α} {a₂ : α} [t2_space α] : has_sum f a₁ → has_sum f a₂ → a₁ = a₂ :=
  tendsto_nhds_unique

theorem summable.has_sum_iff_tendsto_nat {α : Type u_1} [add_comm_monoid α] [topological_space α]
    [t2_space α] {f : ℕ → α} {a : α} (hf : summable f) :
    has_sum f a ↔
        filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top
          (nhds a) :=
  sorry

theorem equiv.summable_iff_of_has_sum_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [add_comm_monoid α] [topological_space α] {α' : Type u_4} [add_comm_monoid α']
    [topological_space α'] (e : α' ≃ α) {f : β → α} {g : γ → α'}
    (he : ∀ {a : α'}, has_sum f (coe_fn e a) ↔ has_sum g a) : summable f ↔ summable g :=
  sorry

theorem has_sum.add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} {g : β → α} {a : α} {b : α} [has_continuous_add α] (hf : has_sum f a)
    (hg : has_sum g b) : has_sum (fun (b : β) => f b + g b) (a + b) :=
  sorry

theorem summable.add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} {g : β → α} [has_continuous_add α] (hf : summable f) (hg : summable g) :
    summable fun (b : β) => f b + g b :=
  has_sum.summable (has_sum.add (summable.has_sum hf) (summable.has_sum hg))

theorem has_sum_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [has_continuous_add α] {f : γ → β → α} {a : γ → α} {s : finset γ} :
    (∀ (i : γ), i ∈ s → has_sum (f i) (a i)) →
        has_sum (fun (b : β) => finset.sum s fun (i : γ) => f i b)
          (finset.sum s fun (i : γ) => a i) :=
  sorry

theorem summable_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [has_continuous_add α] {f : γ → β → α} {s : finset γ}
    (hf : ∀ (i : γ), i ∈ s → summable (f i)) :
    summable fun (b : β) => finset.sum s fun (i : γ) => f i b :=
  has_sum.summable (has_sum_sum fun (i : γ) (hi : i ∈ s) => summable.has_sum (hf i hi))

theorem has_sum.add_compl {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} {a : α} {b : α} [has_continuous_add α] {s : set β} (ha : has_sum (f ∘ coe) a)
    (hb : has_sum (f ∘ coe) b) : has_sum f (a + b) :=
  sorry

theorem summable.add_compl {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} [has_continuous_add α] {s : set β} (hs : summable (f ∘ coe))
    (hsc : summable (f ∘ coe)) : summable f :=
  has_sum.summable (has_sum.add_compl (summable.has_sum hs) (summable.has_sum hsc))

theorem has_sum.compl_add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} {a : α} {b : α} [has_continuous_add α] {s : set β} (ha : has_sum (f ∘ coe) a)
    (hb : has_sum (f ∘ coe) b) : has_sum f (a + b) :=
  sorry

theorem summable.compl_add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    {f : β → α} [has_continuous_add α] {s : set β} (hs : summable (f ∘ coe))
    (hsc : summable (f ∘ coe)) : summable f :=
  has_sum.summable (has_sum.compl_add (summable.has_sum hs) (summable.has_sum hsc))

theorem has_sum.sigma {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [has_continuous_add α] [regular_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α}
    {g : β → α} {a : α} (ha : has_sum f a)
    (hf : ∀ (b : β), has_sum (fun (c : γ b) => f (sigma.mk b c)) (g b)) : has_sum g a :=
  sorry

/-- If a series `f` on `β × γ` has sum `a` and for each `b` the restriction of `f` to `{b} × γ`
has sum `g b`, then the series `g` has sum `a`. -/
theorem has_sum.prod_fiberwise {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [has_continuous_add α] [regular_space α] {f : β × γ → α} {g : β → α}
    {a : α} (ha : has_sum f a) (hf : ∀ (b : β), has_sum (fun (c : γ) => f (b, c)) (g b)) :
    has_sum g a :=
  has_sum.sigma (iff.mpr (equiv.has_sum_iff (equiv.sigma_equiv_prod β γ)) ha) hf

theorem summable.sigma' {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [has_continuous_add α] [regular_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α}
    (ha : summable f) (hf : ∀ (b : β), summable fun (c : γ b) => f (sigma.mk b c)) :
    summable fun (b : β) => tsum fun (c : γ b) => f (sigma.mk b c) :=
  has_sum.summable (has_sum.sigma (summable.has_sum ha) fun (b : β) => summable.has_sum (hf b))

theorem has_sum.sigma_of_has_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] [has_continuous_add α] [regular_space α] {γ : β → Type u_3}
    {f : (sigma fun (b : β) => γ b) → α} {g : β → α} {a : α} (ha : has_sum g a)
    (hf : ∀ (b : β), has_sum (fun (c : γ b) => f (sigma.mk b c)) (g b)) (hf' : summable f) :
    has_sum f a :=
  sorry

theorem has_sum.tsum_eq {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] {f : β → α} {a : α} (ha : has_sum f a) : (tsum fun (b : β) => f b) = a :=
  has_sum.unique (summable.has_sum (Exists.intro a ha)) ha

theorem summable.has_sum_iff {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] {f : β → α} {a : α} (h : summable f) :
    has_sum f a ↔ (tsum fun (b : β) => f b) = a :=
  { mp := has_sum.tsum_eq,
    mpr := fun (eq : (tsum fun (b : β) => f b) = a) => eq ▸ summable.has_sum h }

@[simp] theorem tsum_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] : (tsum fun (b : β) => 0) = 0 :=
  has_sum.tsum_eq has_sum_zero

theorem tsum_eq_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] {f : β → α} {s : finset β} (hf : ∀ (b : β), ¬b ∈ s → f b = 0) :
    (tsum fun (b : β) => f b) = finset.sum s fun (b : β) => f b :=
  has_sum.tsum_eq (has_sum_sum_of_ne_finset_zero hf)

theorem tsum_fintype {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] [fintype β] (f : β → α) :
    (tsum fun (b : β) => f b) = finset.sum finset.univ fun (b : β) => f b :=
  has_sum.tsum_eq (has_sum_fintype f)

@[simp] theorem finset.tsum_subtype {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] [t2_space α] (s : finset β) (f : β → α) :
    (tsum fun (x : Subtype fun (x : β) => x ∈ s) => f ↑x) = finset.sum s fun (x : β) => f x :=
  has_sum.tsum_eq (finset.has_sum s f)

@[simp] theorem finset.tsum_subtype' {α : Type u_1} {β : Type u_2} [add_comm_monoid α]
    [topological_space α] [t2_space α] (s : finset β) (f : β → α) :
    (tsum fun (x : ↥↑s) => f ↑x) = finset.sum s fun (x : β) => f x :=
  finset.tsum_subtype s f

theorem tsum_eq_single {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] {f : β → α} (b : β) (hf : ∀ (b' : β), b' ≠ b → f b' = 0) :
    (tsum fun (b : β) => f b) = f b :=
  has_sum.tsum_eq (has_sum_single b hf)

@[simp] theorem tsum_ite_eq {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] (b : β) (a : α) : (tsum fun (b' : β) => ite (b' = b) a 0) = a :=
  has_sum.tsum_eq (has_sum_ite_eq b a)

theorem equiv.tsum_eq_tsum_of_has_sum_iff_has_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [add_comm_monoid α] [topological_space α] [t2_space α] {α' : Type u_4} [add_comm_monoid α']
    [topological_space α'] (e : α' ≃ α) (h0 : coe_fn e 0 = 0) {f : β → α} {g : γ → α'}
    (h : ∀ {a : α'}, has_sum f (coe_fn e a) ↔ has_sum g a) :
    (tsum fun (b : β) => f b) = coe_fn e (tsum fun (c : γ) => g c) :=
  sorry

theorem tsum_eq_tsum_of_has_sum_iff_has_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} {g : γ → α}
    (h : ∀ {a : α}, has_sum f a ↔ has_sum g a) :
    (tsum fun (b : β) => f b) = tsum fun (c : γ) => g c :=
  equiv.tsum_eq_tsum_of_has_sum_iff_has_sum (equiv.refl α) rfl h

theorem equiv.tsum_eq {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [t2_space α] (j : γ ≃ β) (f : β → α) :
    (tsum fun (c : γ) => f (coe_fn j c)) = tsum fun (b : β) => f b :=
  tsum_eq_tsum_of_has_sum_iff_has_sum fun (a : α) => equiv.has_sum_iff j

theorem equiv.tsum_eq_tsum_of_support {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} {g : γ → α}
    (e : ↥(function.support f) ≃ ↥(function.support g))
    (he : ∀ (x : ↥(function.support f)), g ↑(coe_fn e x) = f ↑x) :
    (tsum fun (x : β) => f x) = tsum fun (y : γ) => g y :=
  tsum_eq_tsum_of_has_sum_iff_has_sum fun (_x : α) => equiv.has_sum_iff_of_support e he

theorem tsum_eq_tsum_of_ne_zero_bij {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [t2_space α] {f : β → α} {g : γ → α} (i : ↥(function.support g) → β)
    (hi : ∀ {x y : ↥(function.support g)}, i x = i y → ↑x = ↑y)
    (hf : function.support f ⊆ set.range i) (hfg : ∀ (x : ↥(function.support g)), f (i x) = g ↑x) :
    (tsum fun (x : β) => f x) = tsum fun (y : γ) => g y :=
  tsum_eq_tsum_of_has_sum_iff_has_sum fun (_x : α) => has_sum_iff_has_sum_of_ne_zero_bij i hi hf hfg

theorem tsum_subtype {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] (s : set β) (f : β → α) :
    (tsum fun (x : ↥s) => f ↑x) = tsum fun (x : β) => set.indicator s f x :=
  tsum_eq_tsum_of_has_sum_iff_has_sum fun (_x : α) => has_sum_subtype_iff_indicator

theorem tsum_add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] {f : β → α} {g : β → α} [has_continuous_add α] (hf : summable f)
    (hg : summable g) :
    (tsum fun (b : β) => f b + g b) = (tsum fun (b : β) => f b) + tsum fun (b : β) => g b :=
  has_sum.tsum_eq (has_sum.add (summable.has_sum hf) (summable.has_sum hg))

theorem tsum_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [t2_space α] [has_continuous_add α] {f : γ → β → α} {s : finset γ}
    (hf : ∀ (i : γ), i ∈ s → summable (f i)) :
    (tsum fun (b : β) => finset.sum s fun (i : γ) => f i b) =
        finset.sum s fun (i : γ) => tsum fun (b : β) => f i b :=
  has_sum.tsum_eq (has_sum_sum fun (i : γ) (hi : i ∈ s) => summable.has_sum (hf i hi))

theorem tsum_sigma' {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] [has_continuous_add α] [regular_space α] {γ : β → Type u_3}
    {f : (sigma fun (b : β) => γ b) → α}
    (h₁ : ∀ (b : β), summable fun (c : γ b) => f (sigma.mk b c)) (h₂ : summable f) :
    (tsum fun (p : sigma fun (b : β) => γ b) => f p) =
        tsum fun (b : β) => tsum fun (c : γ b) => f (sigma.mk b c) :=
  Eq.symm
    (has_sum.tsum_eq (has_sum.sigma (summable.has_sum h₂) fun (b : β) => summable.has_sum (h₁ b)))

theorem tsum_prod' {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [t2_space α] [has_continuous_add α] [regular_space α] {f : β × γ → α}
    (h : summable f) (h₁ : ∀ (b : β), summable fun (c : γ) => f (b, c)) :
    (tsum fun (p : β × γ) => f p) = tsum fun (b : β) => tsum fun (c : γ) => f (b, c) :=
  Eq.symm
    (has_sum.tsum_eq
      (has_sum.prod_fiberwise (summable.has_sum h) fun (b : β) => summable.has_sum (h₁ b)))

theorem tsum_comm' {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [t2_space α] [has_continuous_add α] [regular_space α] {f : β → γ → α}
    (h : summable (function.uncurry f)) (h₁ : ∀ (b : β), summable (f b))
    (h₂ : ∀ (c : γ), summable fun (b : β) => f b c) :
    (tsum fun (c : γ) => tsum fun (b : β) => f b c) =
        tsum fun (b : β) => tsum fun (c : γ) => f b c :=
  sorry

/-- You can compute a sum over an encodably type by summing over the natural numbers and
  taking a supremum. This is useful for outer measures. -/
theorem tsum_supr_decode2 {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [t2_space α] [encodable γ] [complete_lattice β] (m : β → α) (m0 : m ⊥ = 0)
    (s : γ → β) :
    (tsum fun (i : ℕ) => m (supr fun (b : γ) => supr fun (H : b ∈ encodable.decode2 γ i) => s b)) =
        tsum fun (b : γ) => m (s b) :=
  sorry

/-- `tsum_supr_decode2` specialized to the complete lattice of sets. -/
theorem tsum_Union_decode2 {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [t2_space α] [encodable γ] (m : set β → α) (m0 : m ∅ = 0)
    (s : γ → set β) :
    (tsum
          fun (i : ℕ) =>
            m (set.Union fun (b : γ) => set.Union fun (H : b ∈ encodable.decode2 γ i) => s b)) =
        tsum fun (b : γ) => m (s b) :=
  tsum_supr_decode2 m m0 s

/-! Some properties about measure-like functions.
  These could also be functions defined on complete sublattices of sets, with the property
  that they are countably sub-additive.
  `R` will probably be instantiated with `(≤)` in all applications.
-/

/-- If a function is countably sub-additive then it is sub-additive on encodable types -/
theorem rel_supr_tsum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α]
    [topological_space α] [t2_space α] [encodable γ] [complete_lattice β] (m : β → α) (m0 : m ⊥ = 0)
    (R : α → α → Prop)
    (m_supr : ∀ (s : ℕ → β), R (m (supr fun (i : ℕ) => s i)) (tsum fun (i : ℕ) => m (s i)))
    (s : γ → β) : R (m (supr fun (b : γ) => s b)) (tsum fun (b : γ) => m (s b)) :=
  sorry

/-- If a function is countably sub-additive then it is sub-additive on finite sets -/
theorem rel_supr_sum {α : Type u_1} {β : Type u_2} {δ : Type u_4} [add_comm_monoid α]
    [topological_space α] [t2_space α] [complete_lattice β] (m : β → α) (m0 : m ⊥ = 0)
    (R : α → α → Prop)
    (m_supr : ∀ (s : ℕ → β), R (m (supr fun (i : ℕ) => s i)) (tsum fun (i : ℕ) => m (s i)))
    (s : δ → β) (t : finset δ) :
    R (m (supr fun (d : δ) => supr fun (H : d ∈ t) => s d)) (finset.sum t fun (d : δ) => m (s d)) :=
  sorry

/-- If a function is countably sub-additive then it is binary sub-additive -/
theorem rel_sup_add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α]
    [t2_space α] [complete_lattice β] (m : β → α) (m0 : m ⊥ = 0) (R : α → α → Prop)
    (m_supr : ∀ (s : ℕ → β), R (m (supr fun (i : ℕ) => s i)) (tsum fun (i : ℕ) => m (s i))) (s₁ : β)
    (s₂ : β) : R (m (s₁ ⊔ s₂)) (m s₁ + m s₂) :=
  sorry

theorem pi.has_sum {α : Type u_1} {ι : Type u_5} {π : α → Type u_6}
    [(x : α) → add_comm_monoid (π x)] [(x : α) → topological_space (π x)] {f : ι → (x : α) → π x}
    {g : (x : α) → π x} : has_sum f g ↔ ∀ (x : α), has_sum (fun (i : ι) => f i x) (g x) :=
  sorry

theorem pi.summable {α : Type u_1} {ι : Type u_5} {π : α → Type u_6}
    [(x : α) → add_comm_monoid (π x)] [(x : α) → topological_space (π x)] {f : ι → (x : α) → π x} :
    summable f ↔ ∀ (x : α), summable fun (i : ι) => f i x :=
  sorry

theorem tsum_apply {α : Type u_1} {ι : Type u_5} {π : α → Type u_6}
    [(x : α) → add_comm_monoid (π x)] [(x : α) → topological_space (π x)]
    [∀ (x : α), t2_space (π x)] {f : ι → (x : α) → π x} {x : α} (hf : summable f) :
    tsum (fun (i : ι) => f i) x = tsum fun (i : ι) => f i x :=
  Eq.symm (has_sum.tsum_eq (iff.mp pi.has_sum (summable.has_sum hf) x))

-- `by simpa using` speeds up elaboration. Why?

theorem has_sum.neg {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} {a : α} (h : has_sum f a) :
    has_sum (fun (b : β) => -f b) (-a) :=
  eq.mpr (id (Eq.refl (has_sum (fun (b : β) => -f b) (-a))))
    (eq.mp (Eq.refl (has_sum (⇑(-add_monoid_hom.id α) ∘ f) (coe_fn (-add_monoid_hom.id α) a)))
      (has_sum.map h (-add_monoid_hom.id α) continuous_neg))

theorem summable.neg {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} (hf : summable f) : summable fun (b : β) => -f b :=
  has_sum.summable (has_sum.neg (summable.has_sum hf))

theorem summable.of_neg {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} (hf : summable fun (b : β) => -f b) : summable f :=
  sorry

theorem summable_neg_iff {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} : (summable fun (b : β) => -f b) ↔ summable f :=
  { mp := summable.of_neg, mpr := summable.neg }

theorem has_sum.sub {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} {g : β → α} {a₁ : α} {a₂ : α} (hf : has_sum f a₁)
    (hg : has_sum g a₂) : has_sum (fun (b : β) => f b - g b) (a₁ - a₂) :=
  sorry

theorem summable.sub {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} {g : β → α} (hf : summable f) (hg : summable g) :
    summable fun (b : β) => f b - g b :=
  has_sum.summable (has_sum.sub (summable.has_sum hf) (summable.has_sum hg))

theorem has_sum.has_sum_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [topological_space α] [topological_add_group α] {f : β → α} {a₁ : α} {a₂ : α} {s : set β}
    (hf : has_sum (f ∘ coe) a₁) : has_sum (f ∘ coe) a₂ ↔ has_sum f (a₁ + a₂) :=
  sorry

theorem has_sum.has_sum_iff_compl {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [topological_space α] [topological_add_group α] {f : β → α} {a₁ : α} {a₂ : α} {s : set β}
    (hf : has_sum (f ∘ coe) a₁) : has_sum f a₂ ↔ has_sum (f ∘ coe) (a₂ - a₁) :=
  sorry

theorem summable.summable_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [topological_space α] [topological_add_group α] {f : β → α} {s : set β}
    (hf : summable (f ∘ coe)) : summable (f ∘ coe) ↔ summable f :=
  sorry

protected theorem finset.has_sum_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [topological_space α] [topological_add_group α] {f : β → α} {a : α} (s : finset β) :
    has_sum (fun (x : Subtype fun (x : β) => ¬x ∈ s) => f ↑x) a ↔
        has_sum f (a + finset.sum s fun (i : β) => f i) :=
  sorry

protected theorem finset.has_sum_iff_compl {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [topological_space α] [topological_add_group α] {f : β → α} {a : α} (s : finset β) :
    has_sum f a ↔
        has_sum (fun (x : Subtype fun (x : β) => ¬x ∈ s) => f ↑x)
          (a - finset.sum s fun (i : β) => f i) :=
  has_sum.has_sum_iff_compl (finset.has_sum s f)

protected theorem finset.summable_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [topological_space α] [topological_add_group α] {f : β → α} (s : finset β) :
    (summable fun (x : Subtype fun (x : β) => ¬x ∈ s) => f ↑x) ↔ summable f :=
  summable.summable_compl_iff (finset.summable s f)

theorem set.finite.summable_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [topological_space α] [topological_add_group α] {f : β → α} {s : set β} (hs : set.finite s) :
    summable (f ∘ coe) ↔ summable f :=
  summable.summable_compl_iff (set.finite.summable hs f)

theorem tsum_neg {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} [t2_space α] (hf : summable f) :
    (tsum fun (b : β) => -f b) = -tsum fun (b : β) => f b :=
  has_sum.tsum_eq (has_sum.neg (summable.has_sum hf))

theorem tsum_sub {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} {g : β → α} [t2_space α] (hf : summable f)
    (hg : summable g) :
    (tsum fun (b : β) => f b - g b) = (tsum fun (b : β) => f b) - tsum fun (b : β) => g b :=
  has_sum.tsum_eq (has_sum.sub (summable.has_sum hf) (summable.has_sum hg))

theorem tsum_add_tsum_compl {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} [t2_space α] {s : set β} (hs : summable (f ∘ coe))
    (hsc : summable (f ∘ coe)) :
    ((tsum fun (x : ↥s) => f ↑x) + tsum fun (x : ↥(sᶜ)) => f ↑x) = tsum fun (x : β) => f x :=
  Eq.symm (has_sum.tsum_eq (has_sum.add_compl (summable.has_sum hs) (summable.has_sum hsc)))

theorem sum_add_tsum_compl {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : β → α} [t2_space α] {s : finset β} (hf : summable f) :
    ((finset.sum s fun (x : β) => f x) + tsum fun (x : ↥(↑sᶜ)) => f ↑x) = tsum fun (x : β) => f x :=
  Eq.symm
    (has_sum.tsum_eq
      (has_sum.add_compl (finset.has_sum s f)
        (summable.has_sum (iff.mpr (finset.summable_compl_iff s) hf))))

/-!
### Sums on subtypes

If `s` is a finset of `α`, we show that the summability of `f` in the whole space and on the subtype
`univ - s` are equivalent, and relate their sums. For a function defined on `ℕ`, we deduce the
formula `(∑ i in range k, f i) + (∑' i, f (i + k)) = (∑' i, f i)`, in `sum_add_tsum_nat_add`.
-/

theorem has_sum_nat_add_iff {α : Type u_1} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : ℕ → α} (k : ℕ) {a : α} :
    has_sum (fun (n : ℕ) => f (n + k)) a ↔
        has_sum f (a + finset.sum (finset.range k) fun (i : ℕ) => f i) :=
  sorry

theorem summable_nat_add_iff {α : Type u_1} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : ℕ → α} (k : ℕ) :
    (summable fun (n : ℕ) => f (n + k)) ↔ summable f :=
  iff.symm
    (equiv.summable_iff_of_has_sum_iff
      (equiv.add_right (finset.sum (finset.range k) fun (i : ℕ) => f i))
      fun (a : α) => iff.symm (has_sum_nat_add_iff k))

theorem has_sum_nat_add_iff' {α : Type u_1} [add_comm_group α] [topological_space α]
    [topological_add_group α] {f : ℕ → α} (k : ℕ) {a : α} :
    has_sum (fun (n : ℕ) => f (n + k)) (a - finset.sum (finset.range k) fun (i : ℕ) => f i) ↔
        has_sum f a :=
  sorry

theorem sum_add_tsum_nat_add {α : Type u_1} [add_comm_group α] [topological_space α]
    [topological_add_group α] [t2_space α] {f : ℕ → α} (k : ℕ) (h : summable f) :
    ((finset.sum (finset.range k) fun (i : ℕ) => f i) + tsum fun (i : ℕ) => f (i + k)) =
        tsum fun (i : ℕ) => f i :=
  sorry

theorem tsum_eq_zero_add {α : Type u_1} [add_comm_group α] [topological_space α]
    [topological_add_group α] [t2_space α] {f : ℕ → α} (hf : summable f) :
    (tsum fun (b : ℕ) => f b) = f 0 + tsum fun (b : ℕ) => f (b + 1) :=
  sorry

/-- For `f : ℕ → α`, then `∑' k, f (k + i)` tends to zero. This does not require a summability
assumption on `f`, as otherwise all sums are zero. -/
theorem tendsto_sum_nat_add {α : Type u_1} [add_comm_group α] [topological_space α]
    [topological_add_group α] [t2_space α] (f : ℕ → α) :
    filter.tendsto (fun (i : ℕ) => tsum fun (k : ℕ) => f (k + i)) filter.at_top (nhds 0) :=
  sorry

theorem has_sum.mul_left {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α]
    [topological_semiring α] {f : β → α} {a₁ : α} (a₂ : α) (h : has_sum f a₁) :
    has_sum (fun (b : β) => a₂ * f b) (a₂ * a₁) :=
  eq.mpr (id (Eq.refl (has_sum (fun (b : β) => a₂ * f b) (a₂ * a₁))))
    (eq.mp
      (Eq.refl
        (has_sum (⇑(add_monoid_hom.mul_left a₂) ∘ f) (coe_fn (add_monoid_hom.mul_left a₂) a₁)))
      (has_sum.map h (add_monoid_hom.mul_left a₂) (continuous.mul continuous_const continuous_id)))

theorem has_sum.mul_right {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α]
    [topological_semiring α] {f : β → α} {a₁ : α} (a₂ : α) (hf : has_sum f a₁) :
    has_sum (fun (b : β) => f b * a₂) (a₁ * a₂) :=
  eq.mpr (id (Eq.refl (has_sum (fun (b : β) => f b * a₂) (a₁ * a₂))))
    (eq.mp
      (Eq.refl
        (has_sum (⇑(add_monoid_hom.mul_right a₂) ∘ f) (coe_fn (add_monoid_hom.mul_right a₂) a₁)))
      (has_sum.map hf (add_monoid_hom.mul_right a₂)
        (continuous.mul continuous_id continuous_const)))

theorem summable.mul_left {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α]
    [topological_semiring α] {f : β → α} (a : α) (hf : summable f) :
    summable fun (b : β) => a * f b :=
  has_sum.summable (has_sum.mul_left a (summable.has_sum hf))

theorem summable.mul_right {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α]
    [topological_semiring α] {f : β → α} (a : α) (hf : summable f) :
    summable fun (b : β) => f b * a :=
  has_sum.summable (has_sum.mul_right a (summable.has_sum hf))

theorem summable.tsum_mul_left {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α]
    [topological_semiring α] {f : β → α} [t2_space α] (a : α) (hf : summable f) :
    (tsum fun (b : β) => a * f b) = a * tsum fun (b : β) => f b :=
  has_sum.tsum_eq (has_sum.mul_left a (summable.has_sum hf))

theorem summable.tsum_mul_right {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α]
    [topological_semiring α] {f : β → α} [t2_space α] (a : α) (hf : summable f) :
    (tsum fun (b : β) => f b * a) = (tsum fun (b : β) => f b) * a :=
  has_sum.tsum_eq (has_sum.mul_right a (summable.has_sum hf))

theorem has_sum.smul {α : Type u_1} {β : Type u_2} {R : Type u_5} [semiring R] [topological_space R]
    [topological_space α] [add_comm_monoid α] [semimodule R α] [topological_semimodule R α]
    {f : β → α} {a : α} {r : R} (hf : has_sum f a) : has_sum (fun (z : β) => r • f z) (r • a) :=
  has_sum.map hf (const_smul_hom α r) (continuous.smul continuous_const continuous_id)

theorem summable.smul {α : Type u_1} {β : Type u_2} {R : Type u_5} [semiring R]
    [topological_space R] [topological_space α] [add_comm_monoid α] [semimodule R α]
    [topological_semimodule R α] {f : β → α} {r : R} (hf : summable f) :
    summable fun (z : β) => r • f z :=
  has_sum.summable (has_sum.smul (summable.has_sum hf))

theorem tsum_smul {α : Type u_1} {β : Type u_2} {R : Type u_5} [semiring R] [topological_space R]
    [topological_space α] [add_comm_monoid α] [semimodule R α] [topological_semimodule R α]
    {f : β → α} [t2_space α] {r : R} (hf : summable f) :
    (tsum fun (z : β) => r • f z) = r • tsum fun (z : β) => f z :=
  has_sum.tsum_eq (has_sum.smul (summable.has_sum hf))

theorem has_sum.div_const {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α]
    [topological_semiring α] {f : β → α} {a : α} (h : has_sum f a) (b : α) :
    has_sum (fun (x : β) => f x / b) (a / b) :=
  sorry

theorem has_sum_mul_left_iff {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α]
    [topological_semiring α] {f : β → α} {a₁ : α} {a₂ : α} (h : a₂ ≠ 0) :
    has_sum f a₁ ↔ has_sum (fun (b : β) => a₂ * f b) (a₂ * a₁) :=
  sorry

theorem has_sum_mul_right_iff {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α]
    [topological_semiring α] {f : β → α} {a₁ : α} {a₂ : α} (h : a₂ ≠ 0) :
    has_sum f a₁ ↔ has_sum (fun (b : β) => f b * a₂) (a₁ * a₂) :=
  sorry

theorem summable_mul_left_iff {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α]
    [topological_semiring α] {f : β → α} {a : α} (h : a ≠ 0) :
    summable f ↔ summable fun (b : β) => a * f b :=
  sorry

theorem summable_mul_right_iff {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α]
    [topological_semiring α] {f : β → α} {a : α} (h : a ≠ 0) :
    summable f ↔ summable fun (b : β) => f b * a :=
  sorry

theorem tsum_mul_left {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α]
    [topological_semiring α] {f : β → α} {a : α} [t2_space α] :
    (tsum fun (x : β) => a * f x) = a * tsum fun (x : β) => f x :=
  sorry

theorem tsum_mul_right {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α]
    [topological_semiring α] {f : β → α} {a : α} [t2_space α] :
    (tsum fun (x : β) => f x * a) = (tsum fun (x : β) => f x) * a :=
  sorry

theorem has_sum_le {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α]
    [order_closed_topology α] {f : β → α} {g : β → α} {a₁ : α} {a₂ : α} (h : ∀ (b : β), f b ≤ g b)
    (hf : has_sum f a₁) (hg : has_sum g a₂) : a₁ ≤ a₂ :=
  le_of_tendsto_of_tendsto' hf hg
    fun (s : finset β) => finset.sum_le_sum fun (b : β) (_x : b ∈ s) => h b

theorem has_sum_le_inj {α : Type u_1} {β : Type u_2} {γ : Type u_3} [ordered_add_comm_monoid α]
    [topological_space α] [order_closed_topology α] {f : β → α} {a₁ : α} {a₂ : α} {g : γ → α}
    (i : β → γ) (hi : function.injective i) (hs : ∀ (c : γ), ¬c ∈ set.range i → 0 ≤ g c)
    (h : ∀ (b : β), f b ≤ g (i b)) (hf : has_sum f a₁) (hg : has_sum g a₂) : a₁ ≤ a₂ :=
  sorry

theorem tsum_le_tsum_of_inj {α : Type u_1} {β : Type u_2} {γ : Type u_3} [ordered_add_comm_monoid α]
    [topological_space α] [order_closed_topology α] {f : β → α} {g : γ → α} (i : β → γ)
    (hi : function.injective i) (hs : ∀ (c : γ), ¬c ∈ set.range i → 0 ≤ g c)
    (h : ∀ (b : β), f b ≤ g (i b)) (hf : summable f) (hg : summable g) : tsum f ≤ tsum g :=
  has_sum_le_inj i hi hs h (summable.has_sum hf) (summable.has_sum hg)

theorem sum_le_has_sum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α]
    [topological_space α] [order_closed_topology α] {a : α} {f : β → α} (s : finset β)
    (hs : ∀ (b : β), ¬b ∈ s → 0 ≤ f b) (hf : has_sum f a) : (finset.sum s fun (b : β) => f b) ≤ a :=
  sorry

theorem le_has_sum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α]
    [order_closed_topology α] {f : β → α} {a : α} (hf : has_sum f a) (b : β)
    (hb : ∀ (b' : β), b' ≠ b → 0 ≤ f b') : f b ≤ a :=
  sorry

theorem sum_le_tsum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α]
    [order_closed_topology α] {f : β → α} (s : finset β) (hs : ∀ (b : β), ¬b ∈ s → 0 ≤ f b)
    (hf : summable f) : (finset.sum s fun (b : β) => f b) ≤ tsum f :=
  sum_le_has_sum s hs (summable.has_sum hf)

theorem le_tsum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α]
    [order_closed_topology α] {f : β → α} (hf : summable f) (b : β)
    (hb : ∀ (b' : β), b' ≠ b → 0 ≤ f b') : f b ≤ tsum fun (b : β) => f b :=
  le_has_sum (summable.has_sum hf) b hb

theorem tsum_le_tsum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α]
    [order_closed_topology α] {f : β → α} {g : β → α} (h : ∀ (b : β), f b ≤ g b) (hf : summable f)
    (hg : summable g) : (tsum fun (b : β) => f b) ≤ tsum fun (b : β) => g b :=
  has_sum_le h (summable.has_sum hf) (summable.has_sum hg)

theorem has_sum.nonneg {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α]
    [topological_space α] [order_closed_topology α] {g : β → α} {a : α} (h : ∀ (b : β), 0 ≤ g b)
    (ha : has_sum g a) : 0 ≤ a :=
  has_sum_le h has_sum_zero ha

theorem has_sum.nonpos {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α]
    [topological_space α] [order_closed_topology α] {g : β → α} {a : α} (h : ∀ (b : β), g b ≤ 0)
    (ha : has_sum g a) : a ≤ 0 :=
  has_sum_le h ha has_sum_zero

theorem tsum_nonneg {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α]
    [order_closed_topology α] {g : β → α} (h : ∀ (b : β), 0 ≤ g b) : 0 ≤ tsum fun (b : β) => g b :=
  sorry

theorem tsum_nonpos {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α]
    [order_closed_topology α] {f : β → α} (h : ∀ (b : β), f b ≤ 0) :
    (tsum fun (b : β) => f b) ≤ 0 :=
  sorry

theorem le_has_sum' {α : Type u_1} {β : Type u_2} [canonically_ordered_add_monoid α]
    [topological_space α] [order_closed_topology α] {f : β → α} {a : α} (hf : has_sum f a) (b : β) :
    f b ≤ a :=
  le_has_sum hf b fun (_x : β) (_x_1 : _x ≠ b) => zero_le (f _x)

theorem le_tsum' {α : Type u_1} {β : Type u_2} [canonically_ordered_add_monoid α]
    [topological_space α] [order_closed_topology α] {f : β → α} (hf : summable f) (b : β) :
    f b ≤ tsum fun (b : β) => f b :=
  le_tsum hf b fun (_x : β) (_x_1 : _x ≠ b) => zero_le (f _x)

theorem has_sum_zero_iff {α : Type u_1} {β : Type u_2} [canonically_ordered_add_monoid α]
    [topological_space α] [order_closed_topology α] {f : β → α} :
    has_sum f 0 ↔ ∀ (x : β), f x = 0 :=
  sorry

theorem tsum_eq_zero_iff {α : Type u_1} {β : Type u_2} [canonically_ordered_add_monoid α]
    [topological_space α] [order_closed_topology α] {f : β → α} (hf : summable f) :
    (tsum fun (i : β) => f i) = 0 ↔ ∀ (x : β), f x = 0 :=
  sorry

theorem summable_iff_cauchy_seq_finset {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [uniform_space α] [complete_space α] {f : β → α} :
    summable f ↔ cauchy_seq fun (s : finset β) => finset.sum s fun (b : β) => f b :=
  iff.symm cauchy_map_iff_exists_tendsto

theorem cauchy_seq_finset_iff_vanishing {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [uniform_space α] [uniform_add_group α] {f : β → α} :
    (cauchy_seq fun (s : finset β) => finset.sum s fun (b : β) => f b) ↔
        ∀ (e : set α),
          e ∈ nhds 0 →
            ∃ (s : finset β),
              ∀ (t : finset β), disjoint t s → (finset.sum t fun (b : β) => f b) ∈ e :=
  sorry

theorem summable_iff_vanishing {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α]
    [uniform_add_group α] {f : β → α} [complete_space α] :
    summable f ↔
        ∀ (e : set α),
          e ∈ nhds 0 →
            ∃ (s : finset β),
              ∀ (t : finset β), disjoint t s → (finset.sum t fun (b : β) => f b) ∈ e :=
  sorry

/- TODO: generalize to monoid with a uniform continuous subtraction operator: `(a + b) - b = a` -/

theorem summable.summable_of_eq_zero_or_self {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [uniform_space α] [uniform_add_group α] {f : β → α} {g : β → α} [complete_space α]
    (hf : summable f) (h : ∀ (b : β), g b = 0 ∨ g b = f b) : summable g :=
  sorry

protected theorem summable.indicator {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [uniform_space α] [uniform_add_group α] {f : β → α} [complete_space α] (hf : summable f)
    (s : set β) : summable (set.indicator s f) :=
  summable.summable_of_eq_zero_or_self hf (set.indicator_eq_zero_or_self s f)

theorem summable.comp_injective {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_group α]
    [uniform_space α] [uniform_add_group α] {f : β → α} [complete_space α] {i : γ → β}
    (hf : summable f) (hi : function.injective i) : summable (f ∘ i) :=
  sorry

theorem summable.subtype {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α]
    [uniform_add_group α] {f : β → α} [complete_space α] (hf : summable f) (s : set β) :
    summable (f ∘ coe) :=
  summable.comp_injective hf subtype.coe_injective

theorem summable_subtype_and_compl {α : Type u_1} {β : Type u_2} [add_comm_group α]
    [uniform_space α] [uniform_add_group α] {f : β → α} [complete_space α] {s : set β} :
    ((summable fun (x : ↥s) => f ↑x) ∧ summable fun (x : ↥(sᶜ)) => f ↑x) ↔ summable f :=
  { mp := iff.mpr and_imp summable.add_compl,
    mpr :=
      fun (h : summable f) => { left := summable.subtype h s, right := summable.subtype h (sᶜ) } }

theorem summable.sigma_factor {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α]
    [uniform_add_group α] [complete_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α}
    (ha : summable f) (b : β) : summable fun (c : γ b) => f (sigma.mk b c) :=
  summable.comp_injective ha sigma_mk_injective

theorem summable.sigma {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α]
    [uniform_add_group α] [complete_space α] [regular_space α] {γ : β → Type u_3}
    {f : (sigma fun (b : β) => γ b) → α} (ha : summable f) :
    summable fun (b : β) => tsum fun (c : γ b) => f (sigma.mk b c) :=
  summable.sigma' ha fun (b : β) => summable.sigma_factor ha b

theorem summable.prod_factor {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_group α]
    [uniform_space α] [uniform_add_group α] [complete_space α] {f : β × γ → α} (h : summable f)
    (b : β) : summable fun (c : γ) => f (b, c) :=
  summable.comp_injective h
    fun (c₁ c₂ : γ) (h : (fun (c : γ) => (b, c)) c₁ = (fun (c : γ) => (b, c)) c₂) =>
      and.right (iff.mp prod.ext_iff h)

theorem tsum_sigma {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α]
    [uniform_add_group α] [complete_space α] [regular_space α] {γ : β → Type u_3}
    {f : (sigma fun (b : β) => γ b) → α} (ha : summable f) :
    (tsum fun (p : sigma fun (b : β) => γ b) => f p) =
        tsum fun (b : β) => tsum fun (c : γ b) => f (sigma.mk b c) :=
  tsum_sigma' (fun (b : β) => summable.sigma_factor ha b) ha

theorem tsum_prod {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_group α] [uniform_space α]
    [uniform_add_group α] [complete_space α] [regular_space α] {f : β × γ → α} (h : summable f) :
    (tsum fun (p : β × γ) => f p) = tsum fun (b : β) => tsum fun (c : γ) => f (b, c) :=
  tsum_prod' h (summable.prod_factor h)

theorem tsum_comm {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_group α] [uniform_space α]
    [uniform_add_group α] [complete_space α] [regular_space α] {f : β → γ → α}
    (h : summable (function.uncurry f)) :
    (tsum fun (c : γ) => tsum fun (b : β) => f b c) =
        tsum fun (b : β) => tsum fun (c : γ) => f b c :=
  tsum_comm' h (summable.prod_factor h) (summable.prod_factor (summable.prod_symm h))

theorem summable.vanishing {α : Type u_1} {G : Type u_5} [topological_space G] [add_comm_group G]
    [topological_add_group G] {f : α → G} (hf : summable f) {e : set G} (he : e ∈ nhds 0) :
    ∃ (s : finset α), ∀ (t : finset α), disjoint t s → (finset.sum t fun (k : α) => f k) ∈ e :=
  sorry

/-- Series divergence test: if `f` is a convergent series, then `f x` tends to zero along
`cofinite`. -/
theorem summable.tendsto_cofinite_zero {α : Type u_1} {G : Type u_5} [topological_space G]
    [add_comm_group G] [topological_add_group G] {f : α → G} (hf : summable f) :
    filter.tendsto f filter.cofinite (nhds 0) :=
  sorry

theorem summable_abs_iff {α : Type u_1} {β : Type u_2} [linear_ordered_add_comm_group β]
    [uniform_space β] [uniform_add_group β] [complete_space β] {f : α → β} :
    (summable fun (x : α) => abs (f x)) ↔ summable f :=
  sorry

theorem summable.of_abs {α : Type u_1} {β : Type u_2} [linear_ordered_add_comm_group β]
    [uniform_space β] [uniform_add_group β] [complete_space β] {f : α → β} :
    (summable fun (x : α) => abs (f x)) → summable f :=
  iff.mp summable_abs_iff

/-- If the extended distance between consequent points of a sequence is estimated
by a summable series of `nnreal`s, then the original sequence is a Cauchy sequence. -/
theorem cauchy_seq_of_edist_le_of_summable {α : Type u_1} [emetric_space α] {f : ℕ → α}
    (d : ℕ → nnreal) (hf : ∀ (n : ℕ), edist (f n) (f (Nat.succ n)) ≤ ↑(d n)) (hd : summable d) :
    cauchy_seq f :=
  sorry

/-- If the distance between consequent points of a sequence is estimated by a summable series,
then the original sequence is a Cauchy sequence. -/
theorem cauchy_seq_of_dist_le_of_summable {α : Type u_1} [metric_space α] {f : ℕ → α} (d : ℕ → ℝ)
    (hf : ∀ (n : ℕ), dist (f n) (f (Nat.succ n)) ≤ d n) (hd : summable d) : cauchy_seq f :=
  sorry

theorem cauchy_seq_of_summable_dist {α : Type u_1} [metric_space α] {f : ℕ → α}
    (h : summable fun (n : ℕ) => dist (f n) (f (Nat.succ n))) : cauchy_seq f :=
  cauchy_seq_of_dist_le_of_summable (fun (n : ℕ) => dist (f n) (f (Nat.succ n)))
    (fun (_x : ℕ) => le_refl (dist (f _x) (f (Nat.succ _x)))) h

theorem dist_le_tsum_of_dist_le_of_tendsto {α : Type u_1} [metric_space α] {f : ℕ → α} (d : ℕ → ℝ)
    (hf : ∀ (n : ℕ), dist (f n) (f (Nat.succ n)) ≤ d n) (hd : summable d) {a : α}
    (ha : filter.tendsto f filter.at_top (nhds a)) (n : ℕ) :
    dist (f n) a ≤ tsum fun (m : ℕ) => d (n + m) :=
  sorry

theorem dist_le_tsum_of_dist_le_of_tendsto₀ {α : Type u_1} [metric_space α] {f : ℕ → α} (d : ℕ → ℝ)
    (hf : ∀ (n : ℕ), dist (f n) (f (Nat.succ n)) ≤ d n) (hd : summable d) {a : α}
    (ha : filter.tendsto f filter.at_top (nhds a)) : dist (f 0) a ≤ tsum d :=
  sorry

theorem dist_le_tsum_dist_of_tendsto {α : Type u_1} [metric_space α] {f : ℕ → α}
    (h : summable fun (n : ℕ) => dist (f n) (f (Nat.succ n))) {a : α}
    (ha : filter.tendsto f filter.at_top (nhds a)) (n : ℕ) :
    dist (f n) a ≤ tsum fun (m : ℕ) => dist (f (n + m)) (f (Nat.succ (n + m))) :=
  (fun
      (this :
      dist (f n) a ≤ tsum fun (m : ℕ) => (fun (n : ℕ) => dist (f n) (f (Nat.succ n))) (n + m)) =>
      this)
    (dist_le_tsum_of_dist_le_of_tendsto (fun (n : ℕ) => dist (f n) (f (Nat.succ n)))
      (fun (_x : ℕ) => le_refl (dist (f _x) (f (Nat.succ _x)))) h ha n)

theorem dist_le_tsum_dist_of_tendsto₀ {α : Type u_1} [metric_space α] {f : ℕ → α}
    (h : summable fun (n : ℕ) => dist (f n) (f (Nat.succ n))) {a : α}
    (ha : filter.tendsto f filter.at_top (nhds a)) :
    dist (f 0) a ≤ tsum fun (n : ℕ) => dist (f n) (f (Nat.succ n)) :=
  sorry

end Mathlib


/-
Copyright (c) 2018 Andreas Swerdlow. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Andreas Swerdlow, Kenny Lau

! This file was ported from Lean 3 source module ring_theory.ring_invo
! leanprover-community/mathlib commit fac369018417f980cec5fcdafc766a69f88d8cfe
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Ring.Equiv
import Mathbin.Algebra.Ring.Opposite

/-!
# Ring involutions

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

This file defines a ring involution as a structure extending `R ≃+* Rᵐᵒᵖ`,
with the additional fact `f.involution : (f (f x).unop).unop = x`.

## Notations

We provide a coercion to a function `R → Rᵐᵒᵖ`.

## References

* <https://en.wikipedia.org/wiki/Involution_(mathematics)#Ring_theory>

## Tags

Ring involution
-/

variable (R : Type _)

#print RingInvo /-
/-- A ring involution -/
structure RingInvo [Semiring R] extends R ≃+* Rᵐᵒᵖ where
  involution' : ∀ x, (to_fun (to_fun x).unop).unop = x
#align ring_invo RingInvo
-/

/-- The equivalence of rings underlying a ring involution. -/
add_decl_doc RingInvo.toRingEquiv

#print RingInvoClass /-
/-- `ring_invo_class F R S` states that `F` is a type of ring involutions.
You should extend this class when you extend `ring_invo`. -/
class RingInvoClass (F : Type _) (R : outParam (Type _)) [Semiring R] extends
  RingEquivClass F R Rᵐᵒᵖ where
  involution : ∀ (f : F) (x), (f (f x).unop).unop = x
#align ring_invo_class RingInvoClass
-/

namespace RingInvo

variable {R} [Semiring R]

instance (R : Type _) [Semiring R] : RingInvoClass (RingInvo R) R
    where
  coe := toFun
  inv := invFun
  coe_injective' e f h₁ h₂ := by
    cases e
    cases f
    congr
  map_add := map_add'
  map_mul := map_mul'
  left_inv := left_inv
  right_inv := right_inv
  involution := involution'

/- warning: ring_invo.mk' -> RingInvo.mk' is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (f : RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))), (forall (r : R), Eq.{succ u1} R (MulOpposite.unop.{u1} R (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (fun (_x : RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) => R -> (MulOpposite.{u1} R)) (RingHom.hasCoeToFun.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) f (MulOpposite.unop.{u1} R (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (fun (_x : RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) => R -> (MulOpposite.{u1} R)) (RingHom.hasCoeToFun.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) f r)))) r) -> (RingInvo.{u1} R _inst_1)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (f : RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))), (forall (r : R), Eq.{succ u1} R (MulOpposite.unop.{u1} R (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => MulOpposite.{u1} R) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) R (MulOpposite.{u1} R) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} (MulOpposite.{u1} R) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (MulOpposite.{u1} R) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) R (MulOpposite.{u1} R) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (MulOpposite.{u1} R) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.instRingHomClassRingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))))) f (MulOpposite.unop.{u1} R (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => MulOpposite.{u1} R) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) R (MulOpposite.{u1} R) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} (MulOpposite.{u1} R) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (MulOpposite.{u1} R) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) R (MulOpposite.{u1} R) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (MulOpposite.{u1} R) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.instRingHomClassRingHom.{u1, u1} R (MulOpposite.{u1} R) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (MulOpposite.nonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))))) f r)))) r) -> (RingInvo.{u1} R _inst_1)
Case conversion may be inaccurate. Consider using '#align ring_invo.mk' RingInvo.mk'ₓ'. -/
/-- Construct a ring involution from a ring homomorphism. -/
def mk' (f : R →+* Rᵐᵒᵖ) (involution : ∀ r, (f (f r).unop).unop = r) : RingInvo R :=
  { f with
    invFun := fun r => (f r.unop).unop
    left_inv := fun r => involution r
    right_inv := fun r => MulOpposite.unop_injective <| involution _
    involution' := involution }
#align ring_invo.mk' RingInvo.mk'

/-- Helper instance for when there's too many metavariables to apply
`fun_like.has_coe_to_fun` directly. -/
instance : CoeFun (RingInvo R) fun _ => R → Rᵐᵒᵖ :=
  ⟨fun f => f.toRingEquiv.toFun⟩

/- warning: ring_invo.to_fun_eq_coe clashes with [anonymous] -> [anonymous]
warning: ring_invo.to_fun_eq_coe -> [anonymous] is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u_1}} [_inst_1 : Semiring.{u_1} R] (f : RingInvo.{u_1} R _inst_1), Eq.{succ u_1} (R -> (MulOpposite.{u_1} R)) (RingInvo.toFun.{u_1} R _inst_1 f) (coeFn.{succ u_1, succ u_1} (RingInvo.{u_1} R _inst_1) (fun (_x : RingInvo.{u_1} R _inst_1) => R -> (MulOpposite.{u_1} R)) (RingInvo.hasCoeToFun.{u_1} R _inst_1) f)
but is expected to have type
  forall {R : Type.{u}} {_inst_1 : Type.{v}}, (Nat -> R -> _inst_1) -> Nat -> (List.{u} R) -> (List.{v} _inst_1)
Case conversion may be inaccurate. Consider using '#align ring_invo.to_fun_eq_coe [anonymous]ₓ'. -/
@[simp]
theorem [anonymous] (f : RingInvo R) : f.toFun = f :=
  rfl
#align ring_invo.to_fun_eq_coe [anonymous]

/- warning: ring_invo.involution -> RingInvo.involution is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (f : RingInvo.{u1} R _inst_1) (x : R), Eq.{succ u1} R (MulOpposite.unop.{u1} R (coeFn.{succ u1, succ u1} (RingInvo.{u1} R _inst_1) (fun (_x : RingInvo.{u1} R _inst_1) => R -> (MulOpposite.{u1} R)) (RingInvo.hasCoeToFun.{u1} R _inst_1) f (MulOpposite.unop.{u1} R (coeFn.{succ u1, succ u1} (RingInvo.{u1} R _inst_1) (fun (_x : RingInvo.{u1} R _inst_1) => R -> (MulOpposite.{u1} R)) (RingInvo.hasCoeToFun.{u1} R _inst_1) f x)))) x
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (f : RingInvo.{u1} R _inst_1) (x : R), Eq.{succ u1} R (MulOpposite.unop.{u1} R (FunLike.coe.{succ u1, succ u1, succ u1} (RingInvo.{u1} R _inst_1) R (fun (_x : R) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : R) => MulOpposite.{u1} R) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (EquivLike.toEmbeddingLike.{succ u1, succ u1, succ u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (MulEquivClass.toEquivLike.{u1, u1, u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (MulOpposite.mul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (RingEquivClass.toMulEquivClass.{u1, u1, u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (MulOpposite.mul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (MulOpposite.add.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) (RingInvoClass.toRingEquivClass.{u1, u1} (RingInvo.{u1} R _inst_1) R _inst_1 (RingInvo.instRingInvoClassRingInvo.{u1} R _inst_1)))))) f (MulOpposite.unop.{u1} R (FunLike.coe.{succ u1, succ u1, succ u1} (RingInvo.{u1} R _inst_1) R (fun (_x : R) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : R) => MulOpposite.{u1} R) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (EquivLike.toEmbeddingLike.{succ u1, succ u1, succ u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (MulEquivClass.toEquivLike.{u1, u1, u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (MulOpposite.mul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (RingEquivClass.toMulEquivClass.{u1, u1, u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (MulOpposite.mul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (MulOpposite.add.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) (RingInvoClass.toRingEquivClass.{u1, u1} (RingInvo.{u1} R _inst_1) R _inst_1 (RingInvo.instRingInvoClassRingInvo.{u1} R _inst_1)))))) f x)))) x
Case conversion may be inaccurate. Consider using '#align ring_invo.involution RingInvo.involutionₓ'. -/
@[simp]
theorem involution (f : RingInvo R) (x : R) : (f (f x).unop).unop = x :=
  f.involution' x
#align ring_invo.involution RingInvo.involution

instance hasCoeToRingEquiv : Coe (RingInvo R) (R ≃+* Rᵐᵒᵖ) :=
  ⟨RingInvo.toRingEquiv⟩
#align ring_invo.has_coe_to_ring_equiv RingInvo.hasCoeToRingEquiv

#print RingInvo.coe_ringEquiv /-
@[norm_cast]
theorem coe_ringEquiv (f : RingInvo R) (a : R) : (f : R ≃+* Rᵐᵒᵖ) a = f a :=
  rfl
#align ring_invo.coe_ring_equiv RingInvo.coe_ringEquiv
-/

/- warning: ring_invo.map_eq_zero_iff -> RingInvo.map_eq_zero_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (f : RingInvo.{u1} R _inst_1) {x : R}, Iff (Eq.{succ u1} (MulOpposite.{u1} R) (coeFn.{succ u1, succ u1} (RingInvo.{u1} R _inst_1) (fun (_x : RingInvo.{u1} R _inst_1) => R -> (MulOpposite.{u1} R)) (RingInvo.hasCoeToFun.{u1} R _inst_1) f x) (OfNat.ofNat.{u1} (MulOpposite.{u1} R) 0 (OfNat.mk.{u1} (MulOpposite.{u1} R) 0 (Zero.zero.{u1} (MulOpposite.{u1} R) (MulOpposite.hasZero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))))))) (Eq.{succ u1} R x (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (f : RingInvo.{u1} R _inst_1) {x : R}, Iff (Eq.{succ u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : R) => MulOpposite.{u1} R) x) (FunLike.coe.{succ u1, succ u1, succ u1} (RingInvo.{u1} R _inst_1) R (fun (_x : R) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : R) => MulOpposite.{u1} R) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (EquivLike.toEmbeddingLike.{succ u1, succ u1, succ u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (MulEquivClass.toEquivLike.{u1, u1, u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (MulOpposite.mul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (RingEquivClass.toMulEquivClass.{u1, u1, u1} (RingInvo.{u1} R _inst_1) R (MulOpposite.{u1} R) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (MulOpposite.mul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (MulOpposite.add.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) (RingInvoClass.toRingEquivClass.{u1, u1} (RingInvo.{u1} R _inst_1) R _inst_1 (RingInvo.instRingInvoClassRingInvo.{u1} R _inst_1)))))) f x) (OfNat.ofNat.{u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : R) => MulOpposite.{u1} R) x) 0 (Zero.toOfNat0.{u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : R) => MulOpposite.{u1} R) x) (MulOpposite.zero.{u1} R (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)))))) (Eq.{succ u1} R x (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)))))
Case conversion may be inaccurate. Consider using '#align ring_invo.map_eq_zero_iff RingInvo.map_eq_zero_iffₓ'. -/
@[simp]
theorem map_eq_zero_iff (f : RingInvo R) {x : R} : f x = 0 ↔ x = 0 :=
  f.toRingEquiv.map_eq_zero_iff
#align ring_invo.map_eq_zero_iff RingInvo.map_eq_zero_iff

end RingInvo

open RingInvo

section CommRing

variable [CommRing R]

#print RingInvo.id /-
/-- The identity function of a `comm_ring` is a ring involution. -/
protected def RingInvo.id : RingInvo R :=
  { RingEquiv.toOpposite R with involution' := fun r => rfl }
#align ring_invo.id RingInvo.id
-/

instance : Inhabited (RingInvo R) :=
  ⟨RingInvo.id _⟩

end CommRing


-- Eliminación de la conjunción en Lean
-- ====================================

-- Demostrar que
--    P ∧ Q → P

import tactic
variables (P Q : Prop)

-- 1ª demostración
example : P ∧ Q → P :=
begin
  intro h,
  cases h with hP hQ,
  exact hP,
end

-- 2ª demostración
example : P ∧ Q → P :=
begin
  rintro ⟨hP, hQ⟩,
  exact hP,
end

-- 3ª demostración
example : P ∧ Q → P :=
begin
  rintro ⟨_, _⟩,
  assumption,
end

-- 4ª demostración
example : P ∧ Q → P :=
λ ⟨hP,_⟩, hP

-- 5ª demostración
example : P ∧ Q → P :=
begin
  assume h : P ∧ Q,
  show P, from h.1,
end

-- 6ª demostración
example : P ∧ Q → P :=
assume h, h.1

-- 7ª demostración
example : P ∧ Q → P :=
and.left

#check and.right

-- 8ª demostración
example : P ∧ Q → P :=
by tauto

-- 9ª demostración
example : P ∧ Q → P :=
by finish


set_option trace.Elab.Deriving.hashable true

inductive SimpleInd
| A
| B
deriving Hashable

theorem «inductive fields have different base hashes» : ∀ x, hash x =
match x with
| SimpleInd.A => 0
| SimpleInd.B => 1 := λ x => rfl
mutual
inductive Foo : Type → Type
| A : Int → (3 = 3) → String → Foo Int
| B : Bar → Foo String
deriving Hashable
inductive Bar
| C
| D : Foo String → Bar
deriving Hashable
end

#eval hash (Foo.A 3 rfl "bla")
#eval hash (Foo.B $ Bar.D $ Foo.B Bar.C)

inductive ManyConstructors | A | B | C | D | E | F | G | H | I | J | K | L
| M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
deriving Hashable

theorem «Each constructor is hashed as a different number to make mixing better» : ∀ x, hash x =
match x with
| ManyConstructors.A => 0
| ManyConstructors.B => 1
| ManyConstructors.C => 2
| ManyConstructors.D => 3
| ManyConstructors.E => 4
| ManyConstructors.F => 5
| ManyConstructors.G => 6
| ManyConstructors.H => 7
| ManyConstructors.I => 8
| ManyConstructors.J => 9
| ManyConstructors.K => 10
| ManyConstructors.L => 11
| ManyConstructors.M => 12
| ManyConstructors.N => 13
| ManyConstructors.O => 14
| ManyConstructors.P => 15
| ManyConstructors.Q => 16
| ManyConstructors.R => 17
| ManyConstructors.S => 18
| ManyConstructors.T => 19
| ManyConstructors.U => 20
| ManyConstructors.V => 21
| ManyConstructors.W => 22
| ManyConstructors.X => 23
| ManyConstructors.Y => 24
| ManyConstructors.Z => 25 := λ x => rfl

structure Person :=
  FirstName : String
  LastName : String
  Age : Nat
deriving Hashable

structure Company :=
  Name : String
  CEO : Person
  NumberOfEmployees : Nat
deriving Hashable

-- structures hash just fine
#eval hash {
  Name := "Microsoft"
  CEO := { FirstName := "Satya", LastName := "Nadella", Age := 53 }
  NumberOfEmployees := 165000 : Company }
-- 10875484723257753924

-- syntax(name := tst) "tst" : command
-- @[command_elab «tst»] def elab_tst : CommandElab := fun stx => do
--   let declNames := #[`Foo, `Bar]
--   let declNames := #[`Foo]
--   discard $ mkHashableHandler declNames
--   pure ()