/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel, Yury Kudryashov

! This file was ported from Lean 3 source module topology.algebra.infinite_sum.real
! leanprover-community/mathlib commit f47581155c818e6361af4e4fda60d27d020c226b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.BigOperators.Intervals
import Mathbin.Topology.Algebra.InfiniteSum.Order
import Mathbin.Topology.Instances.Real

/-!
# Infinite sum in the reals

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

This file provides lemmas about Cauchy sequences in terms of infinite sums.
-/

open Filter Finset

open BigOperators NNReal Topology

variable {α : Type _}

/- warning: cauchy_seq_of_edist_le_of_summable -> cauchySeq_of_edist_le_of_summable is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u1} α] {f : Nat -> α} (d : Nat -> NNReal), (forall (n : Nat), LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder))))) (EDist.edist.{u1} α (PseudoEMetricSpace.toHasEdist.{u1} α _inst_1) (f n) (f (Nat.succ n))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) NNReal ENNReal (HasLiftT.mk.{1, 1} NNReal ENNReal (CoeTCₓ.coe.{1, 1} NNReal ENNReal (coeBase.{1, 1} NNReal ENNReal ENNReal.hasCoe))) (d n))) -> (Summable.{0, 0} NNReal Nat (OrderedCancelAddCommMonoid.toAddCommMonoid.{0} NNReal (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} NNReal NNReal.strictOrderedSemiring)) (UniformSpace.toTopologicalSpace.{0} NNReal (PseudoMetricSpace.toUniformSpace.{0} NNReal NNReal.pseudoMetricSpace)) d) -> (CauchySeq.{u1, 0} α Nat (PseudoEMetricSpace.toUniformSpace.{u1} α _inst_1) (CanonicallyLinearOrderedAddMonoid.semilatticeSup.{0} Nat Nat.canonicallyLinearOrderedAddMonoid) f)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u1} α] {f : Nat -> α} (d : Nat -> NNReal), (forall (n : Nat), LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal))))) (EDist.edist.{u1} α (PseudoEMetricSpace.toEDist.{u1} α _inst_1) (f n) (f (Nat.succ n))) (ENNReal.some (d n))) -> (Summable.{0, 0} NNReal Nat (OrderedCancelAddCommMonoid.toAddCommMonoid.{0} NNReal (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} NNReal instNNRealStrictOrderedSemiring)) (UniformSpace.toTopologicalSpace.{0} NNReal (PseudoMetricSpace.toUniformSpace.{0} NNReal instPseudoMetricSpaceNNReal)) d) -> (CauchySeq.{u1, 0} α Nat (PseudoEMetricSpace.toUniformSpace.{u1} α _inst_1) (Lattice.toSemilatticeSup.{0} Nat Nat.instLatticeNat) f)
Case conversion may be inaccurate. Consider using '#align cauchy_seq_of_edist_le_of_summable cauchySeq_of_edist_le_of_summableₓ'. -/
/-- If the extended distance between consecutive points of a sequence is estimated
by a summable series of `nnreal`s, then the original sequence is a Cauchy sequence. -/
theorem cauchySeq_of_edist_le_of_summable [PseudoEMetricSpace α] {f : ℕ → α} (d : ℕ → ℝ≥0)
    (hf : ∀ n, edist (f n) (f n.succ) ≤ d n) (hd : Summable d) : CauchySeq f :=
  by
  refine' EMetric.cauchySeq_iff_NNReal.2 fun ε εpos => _
  -- Actually we need partial sums of `d` to be a Cauchy sequence
  replace hd : CauchySeq fun n : ℕ => ∑ x in range n, d x :=
    let ⟨_, H⟩ := hd
    H.tendsto_sum_nat.cauchy_seq
  -- Now we take the same `N` as in one of the definitions of a Cauchy sequence
  refine' (Metric.cauchySeq_iff'.1 hd ε (NNReal.coe_pos.2 εpos)).imp fun N hN n hn => _
  have hsum := hN n hn
  -- We simplify the known inequality
  rw [dist_nndist, NNReal.nndist_eq, ← sum_range_add_sum_Ico _ hn, add_tsub_cancel_left] at hsum
  norm_cast  at hsum
  replace hsum := lt_of_le_of_lt (le_max_left _ _) hsum
  rw [edist_comm]
  -- Then use `hf` to simplify the goal to the same form
  apply lt_of_le_of_lt (edist_le_Ico_sum_of_edist_le hn fun k _ _ => hf k)
  assumption_mod_cast
#align cauchy_seq_of_edist_le_of_summable cauchySeq_of_edist_le_of_summable

variable [PseudoMetricSpace α] {f : ℕ → α} {a : α}

/- warning: cauchy_seq_of_dist_le_of_summable -> cauchySeq_of_dist_le_of_summable is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} (d : Nat -> Real), (forall (n : Nat), LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f n) (f (Nat.succ n))) (d n)) -> (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) d) -> (CauchySeq.{u1, 0} α Nat (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1) (CanonicallyLinearOrderedAddMonoid.semilatticeSup.{0} Nat Nat.canonicallyLinearOrderedAddMonoid) f)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} (d : Nat -> Real), (forall (n : Nat), LE.le.{0} Real Real.instLEReal (Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f n) (f (Nat.succ n))) (d n)) -> (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) d) -> (CauchySeq.{u1, 0} α Nat (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1) (Lattice.toSemilatticeSup.{0} Nat Nat.instLatticeNat) f)
Case conversion may be inaccurate. Consider using '#align cauchy_seq_of_dist_le_of_summable cauchySeq_of_dist_le_of_summableₓ'. -/
/-- If the distance between consecutive points of a sequence is estimated by a summable series,
then the original sequence is a Cauchy sequence. -/
theorem cauchySeq_of_dist_le_of_summable (d : ℕ → ℝ) (hf : ∀ n, dist (f n) (f n.succ) ≤ d n)
    (hd : Summable d) : CauchySeq f :=
  by
  refine' Metric.cauchySeq_iff'.2 fun ε εpos => _
  replace hd : CauchySeq fun n : ℕ => ∑ x in range n, d x :=
    let ⟨_, H⟩ := hd
    H.tendsto_sum_nat.cauchy_seq
  refine' (Metric.cauchySeq_iff'.1 hd ε εpos).imp fun N hN n hn => _
  have hsum := hN n hn
  rw [Real.dist_eq, ← sum_Ico_eq_sub _ hn] at hsum
  calc
    dist (f n) (f N) = dist (f N) (f n) := dist_comm _ _
    _ ≤ ∑ x in Ico N n, d x := (dist_le_Ico_sum_of_dist_le hn fun k _ _ => hf k)
    _ ≤ |∑ x in Ico N n, d x| := (le_abs_self _)
    _ < ε := hsum

#align cauchy_seq_of_dist_le_of_summable cauchySeq_of_dist_le_of_summable

#print cauchySeq_of_summable_dist /-
theorem cauchySeq_of_summable_dist (h : Summable fun n => dist (f n) (f n.succ)) : CauchySeq f :=
  cauchySeq_of_dist_le_of_summable _ (fun _ => le_rfl) h
#align cauchy_seq_of_summable_dist cauchySeq_of_summable_dist
-/

/- warning: dist_le_tsum_of_dist_le_of_tendsto -> dist_le_tsum_of_dist_le_of_tendsto is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} (d : Nat -> Real), (forall (n : Nat), LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f n) (f (Nat.succ n))) (d n)) -> (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) d) -> (forall {a : α}, (Filter.Tendsto.{0, u1} Nat α f (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a)) -> (forall (n : Nat), LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f n) a) (tsum.{0, 0} Real Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) Nat (fun (m : Nat) => d (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n m)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} (d : Nat -> Real), (forall (n : Nat), LE.le.{0} Real Real.instLEReal (Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f n) (f (Nat.succ n))) (d n)) -> (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) d) -> (forall {a : α}, (Filter.Tendsto.{0, u1} Nat α f (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a)) -> (forall (n : Nat), LE.le.{0} Real Real.instLEReal (Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f n) a) (tsum.{0, 0} Real Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) Nat (fun (m : Nat) => d (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n m)))))
Case conversion may be inaccurate. Consider using '#align dist_le_tsum_of_dist_le_of_tendsto dist_le_tsum_of_dist_le_of_tendstoₓ'. -/
theorem dist_le_tsum_of_dist_le_of_tendsto (d : ℕ → ℝ) (hf : ∀ n, dist (f n) (f n.succ) ≤ d n)
    (hd : Summable d) {a : α} (ha : Tendsto f atTop (𝓝 a)) (n : ℕ) :
    dist (f n) a ≤ ∑' m, d (n + m) :=
  by
  refine' le_of_tendsto (tendsto_const_nhds.dist ha) (eventually_at_top.2 ⟨n, fun m hnm => _⟩)
  refine' le_trans (dist_le_Ico_sum_of_dist_le hnm fun k _ _ => hf k) _
  rw [sum_Ico_eq_sum_range]
  refine' sum_le_tsum (range _) (fun _ _ => le_trans dist_nonneg (hf _)) _
  exact hd.comp_injective (add_right_injective n)
#align dist_le_tsum_of_dist_le_of_tendsto dist_le_tsum_of_dist_le_of_tendsto

/- warning: dist_le_tsum_of_dist_le_of_tendsto₀ -> dist_le_tsum_of_dist_le_of_tendsto₀ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} {a : α} (d : Nat -> Real), (forall (n : Nat), LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f n) (f (Nat.succ n))) (d n)) -> (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) d) -> (Filter.Tendsto.{0, u1} Nat α f (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a)) -> (LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) a) (tsum.{0, 0} Real Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) Nat d))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} {a : α} (d : Nat -> Real), (forall (n : Nat), LE.le.{0} Real Real.instLEReal (Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f n) (f (Nat.succ n))) (d n)) -> (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) d) -> (Filter.Tendsto.{0, u1} Nat α f (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a)) -> (LE.le.{0} Real Real.instLEReal (Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) a) (tsum.{0, 0} Real Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) Nat d))
Case conversion may be inaccurate. Consider using '#align dist_le_tsum_of_dist_le_of_tendsto₀ dist_le_tsum_of_dist_le_of_tendsto₀ₓ'. -/
theorem dist_le_tsum_of_dist_le_of_tendsto₀ (d : ℕ → ℝ) (hf : ∀ n, dist (f n) (f n.succ) ≤ d n)
    (hd : Summable d) (ha : Tendsto f atTop (𝓝 a)) : dist (f 0) a ≤ tsum d := by
  simpa only [zero_add] using dist_le_tsum_of_dist_le_of_tendsto d hf hd ha 0
#align dist_le_tsum_of_dist_le_of_tendsto₀ dist_le_tsum_of_dist_le_of_tendsto₀

/- warning: dist_le_tsum_dist_of_tendsto -> dist_le_tsum_dist_of_tendsto is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} {a : α}, (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (n : Nat) => Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f n) (f (Nat.succ n)))) -> (Filter.Tendsto.{0, u1} Nat α f (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a)) -> (forall (n : Nat), LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f n) a) (tsum.{0, 0} Real Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) Nat (fun (m : Nat) => Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n m)) (f (Nat.succ (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n m))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} {a : α}, (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (n : Nat) => Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f n) (f (Nat.succ n)))) -> (Filter.Tendsto.{0, u1} Nat α f (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a)) -> (forall (n : Nat), LE.le.{0} Real Real.instLEReal (Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f n) a) (tsum.{0, 0} Real Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) Nat (fun (m : Nat) => Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n m)) (f (Nat.succ (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n m))))))
Case conversion may be inaccurate. Consider using '#align dist_le_tsum_dist_of_tendsto dist_le_tsum_dist_of_tendstoₓ'. -/
theorem dist_le_tsum_dist_of_tendsto (h : Summable fun n => dist (f n) (f n.succ))
    (ha : Tendsto f atTop (𝓝 a)) (n) : dist (f n) a ≤ ∑' m, dist (f (n + m)) (f (n + m).succ) :=
  show dist (f n) a ≤ ∑' m, (fun x => dist (f x) (f x.succ)) (n + m) from
    dist_le_tsum_of_dist_le_of_tendsto (fun n => dist (f n) (f n.succ)) (fun _ => le_rfl) h ha n
#align dist_le_tsum_dist_of_tendsto dist_le_tsum_dist_of_tendsto

/- warning: dist_le_tsum_dist_of_tendsto₀ -> dist_le_tsum_dist_of_tendsto₀ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} {a : α}, (Summable.{0, 0} Real Nat Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (n : Nat) => Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f n) (f (Nat.succ n)))) -> (Filter.Tendsto.{0, u1} Nat α f (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a)) -> (LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) a) (tsum.{0, 0} Real Real.addCommMonoid (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) Nat (fun (n : Nat) => Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) (f n) (f (Nat.succ n)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u1} α] {f : Nat -> α} {a : α}, (Summable.{0, 0} Real Nat Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (fun (n : Nat) => Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f n) (f (Nat.succ n)))) -> (Filter.Tendsto.{0, u1} Nat α f (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) a)) -> (LE.le.{0} Real Real.instLEReal (Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) a) (tsum.{0, 0} Real Real.instAddCommMonoidReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) Nat (fun (n : Nat) => Dist.dist.{u1} α (PseudoMetricSpace.toDist.{u1} α _inst_1) (f n) (f (Nat.succ n)))))
Case conversion may be inaccurate. Consider using '#align dist_le_tsum_dist_of_tendsto₀ dist_le_tsum_dist_of_tendsto₀ₓ'. -/
theorem dist_le_tsum_dist_of_tendsto₀ (h : Summable fun n => dist (f n) (f n.succ))
    (ha : Tendsto f atTop (𝓝 a)) : dist (f 0) a ≤ ∑' n, dist (f n) (f n.succ) := by
  simpa only [zero_add] using dist_le_tsum_dist_of_tendsto h ha 0
#align dist_le_tsum_dist_of_tendsto₀ dist_le_tsum_dist_of_tendsto₀


/-
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, Devon Tuma
-/
import topology.instances.ennreal
import measure_theory.measure.measure_space

/-!
# Probability mass functions

This file is about probability mass functions or discrete probability measures:
a function `α → ℝ≥0∞` such that the values have (infinite) sum `1`.

Construction of monadic `pure` and `bind` is found in `probability_mass_function/monad.lean`,
other constructions of `pmf`s are found in `probability_mass_function/constructions.lean`.

Given `p : pmf α`, `pmf.to_outer_measure` constructs an `outer_measure` on `α`,
by assigning each set the sum of the probabilities of each of its elements.
Under this outer measure, every set is Carathéodory-measurable,
so we can further extend this to a `measure` on `α`, see `pmf.to_measure`.
`pmf.to_measure.is_probability_measure` shows this associated measure is a probability measure.
Conversely, given a probability measure `μ` on a measurable space `α` with all singleton sets
measurable, `μ.to_pmf` constructs a `pmf` on `α`, setting the probability mass of a point `x`
to be the measure of the singleton set `{x}`.

## Tags

probability mass function, discrete probability measure
-/
noncomputable theory
variables {α β γ : Type*}
open_locale classical big_operators nnreal ennreal measure_theory

/-- A probability mass function, or discrete probability measures is a function `α → ℝ≥0∞` such
  that the values have (infinite) sum `1`. -/
def {u} pmf (α : Type u) : Type u := { f : α → ℝ≥0∞ // has_sum f 1 }

namespace pmf

instance fun_like : fun_like (pmf α) α (λ p, ℝ≥0∞) :=
{ coe := λ p a, p.1 a,
  coe_injective' := λ p q h, subtype.eq h }

@[ext] protected lemma ext {p q : pmf α} (h : ∀ x, p x = q x) : p = q := fun_like.ext p q h

lemma ext_iff {p q : pmf α} : p = q ↔ ∀ x, p x = q x := fun_like.ext_iff

lemma has_sum_coe_one (p : pmf α) : has_sum p 1 := p.2

@[simp] lemma tsum_coe (p : pmf α) : ∑' a, p a = 1 := p.has_sum_coe_one.tsum_eq

lemma tsum_coe_ne_top (p : pmf α) : ∑' a, p a ≠ ∞ := p.tsum_coe.symm ▸ ennreal.one_ne_top

lemma tsum_coe_indicator_ne_top (p : pmf α) (s : set α) : ∑' a, s.indicator p a ≠ ∞ :=
ne_of_lt (lt_of_le_of_lt (tsum_le_tsum (λ a, set.indicator_apply_le (λ _, le_rfl))
  ennreal.summable ennreal.summable) (lt_of_le_of_ne le_top p.tsum_coe_ne_top))

@[simp] lemma coe_ne_zero (p : pmf α) : ⇑p ≠ 0 :=
λ hp, zero_ne_one ((tsum_zero.symm.trans (tsum_congr $
  λ x, symm (congr_fun hp x))).trans p.tsum_coe)

/-- The support of a `pmf` is the set where it is nonzero. -/
def support (p : pmf α) : set α := function.support p

@[simp] lemma mem_support_iff (p : pmf α) (a : α) : a ∈ p.support ↔ p a ≠ 0 := iff.rfl

@[simp] lemma support_nonempty (p : pmf α) : p.support.nonempty :=
function.support_nonempty_iff.2 p.coe_ne_zero

lemma apply_eq_zero_iff (p : pmf α) (a : α) : p a = 0 ↔ a ∉ p.support :=
by rw [mem_support_iff, not_not]

lemma apply_pos_iff (p : pmf α) (a : α) : 0 < p a ↔ a ∈ p.support :=
pos_iff_ne_zero.trans (p.mem_support_iff a).symm

lemma apply_eq_one_iff (p : pmf α) (a : α) : p a = 1 ↔ p.support = {a} :=
begin
  refine ⟨λ h, set.subset.antisymm (λ a' ha', by_contra $ λ ha, _) (λ a' ha',
    ha'.symm ▸ (p.mem_support_iff a).2 (λ ha, zero_ne_one $ ha.symm.trans h)), λ h, trans
      (symm $ tsum_eq_single a (λ a' ha', (p.apply_eq_zero_iff a').2 (h.symm ▸ ha'))) p.tsum_coe⟩,
  suffices : 1 < ∑' a, p a,
  from ne_of_lt this p.tsum_coe.symm,
  have : 0 < ∑' b, ite (b = a) 0 (p b),
  from lt_of_le_of_ne' zero_le' ((tsum_ne_zero_iff ennreal.summable).2
    ⟨a', ite_ne_left_iff.2 ⟨ha, ne.symm $ (p.mem_support_iff a').2 ha'⟩⟩),
  calc 1 = 1 + 0 : (add_zero 1).symm ... < p a + ∑' b, ite (b = a) 0 (p b) :
      ennreal.add_lt_add_of_le_of_lt ennreal.one_ne_top (le_of_eq h.symm) this
    ... = ite (a = a) (p a) 0 + ∑' b, ite (b = a) 0 (p b) : by rw [eq_self_iff_true, if_true]
    ... = ∑' b, ite (b = a) (p b) 0 + ∑' b, ite (b = a) 0 (p b) :
      by { congr, exact symm (tsum_eq_single a $ λ b hb, if_neg hb) }
    ... = ∑' b, (ite (b = a) (p b) 0 + ite (b = a) 0 (p b)) : ennreal.tsum_add.symm
    ... = ∑' b, p b : tsum_congr (λ b, by split_ifs; simp only [zero_add, add_zero, le_rfl])
end

lemma coe_le_one (p : pmf α) (a : α) : p a ≤ 1 :=
has_sum_le (by { intro b, split_ifs; simp only [h, zero_le', le_rfl] })
  (has_sum_ite_eq a (p a)) (has_sum_coe_one p)

lemma apply_ne_top (p : pmf α) (a : α) : p a ≠ ∞ :=
ne_of_lt (lt_of_le_of_lt (p.coe_le_one a) ennreal.one_lt_top)

lemma apply_lt_top (p : pmf α) (a : α) : p a < ∞ := lt_of_le_of_ne le_top (p.apply_ne_top a)

section outer_measure

open measure_theory measure_theory.outer_measure

/-- Construct an `outer_measure` from a `pmf`, by assigning measure to each set `s : set α` equal
  to the sum of `p x` for for each `x ∈ α` -/
def to_outer_measure (p : pmf α) : outer_measure α :=
outer_measure.sum (λ (x : α), p x • dirac x)

variables (p : pmf α) (s t : set α)

lemma to_outer_measure_apply : p.to_outer_measure s = ∑' x, s.indicator p x :=
tsum_congr (λ x, smul_dirac_apply (p x) x s)

@[simp] lemma to_outer_measure_caratheodory : p.to_outer_measure.caratheodory = ⊤ :=
begin
  refine (eq_top_iff.2 $ le_trans (le_Inf $ λ x hx, _) (le_sum_caratheodory _)),
  exact let ⟨y, hy⟩ := hx in ((le_of_eq (dirac_caratheodory y).symm).trans
    (le_smul_caratheodory _ _)).trans (le_of_eq hy),
end

@[simp]
lemma to_outer_measure_apply_finset (s : finset α) : p.to_outer_measure s = ∑ x in s, p x :=
begin
  refine (to_outer_measure_apply p s).trans ((@tsum_eq_sum _ _ _ _ _ _ s _).trans _),
  { exact λ x hx, set.indicator_of_not_mem hx _ },
  { exact finset.sum_congr rfl (λ x hx, set.indicator_of_mem hx _) }
end

lemma to_outer_measure_apply_singleton (a : α) : p.to_outer_measure {a} = p a :=
begin
  refine (p.to_outer_measure_apply {a}).trans ((tsum_eq_single a $ λ b hb, _).trans _),
  { exact ite_eq_right_iff.2 (λ hb', false.elim $ hb hb') },
  { exact ite_eq_left_iff.2 (λ ha', false.elim $ ha' rfl) }
end

lemma to_outer_measure_injective : (to_outer_measure : pmf α → outer_measure α).injective :=
λ p q h, pmf.ext (λ x, (p.to_outer_measure_apply_singleton x).symm.trans
  ((congr_fun (congr_arg _ h) _).trans $ q.to_outer_measure_apply_singleton x))

@[simp] lemma to_outer_measure_inj {p q : pmf α} :
  p.to_outer_measure = q.to_outer_measure ↔ p = q := to_outer_measure_injective.eq_iff

lemma to_outer_measure_apply_eq_zero_iff : p.to_outer_measure s = 0 ↔ disjoint p.support s :=
begin
  rw [to_outer_measure_apply, ennreal.tsum_eq_zero],
  exact function.funext_iff.symm.trans set.indicator_eq_zero',
end

@[simp] lemma to_outer_measure_apply_inter_support :
  p.to_outer_measure (s ∩ p.support) = p.to_outer_measure s :=
by simp only [to_outer_measure_apply, pmf.support, set.indicator_inter_support]

/-- Slightly stronger than `outer_measure.mono` having an intersection with `p.support` -/
lemma to_outer_measure_mono {s t : set α} (h : s ∩ p.support ⊆ t) :
  p.to_outer_measure s ≤ p.to_outer_measure t :=
le_trans (le_of_eq (to_outer_measure_apply_inter_support p s).symm) (p.to_outer_measure.mono h)

lemma to_outer_measure_apply_eq_of_inter_support_eq {s t : set α}
  (h : s ∩ p.support = t ∩ p.support) : p.to_outer_measure s = p.to_outer_measure t :=
le_antisymm (p.to_outer_measure_mono (h.symm ▸ (set.inter_subset_left t p.support)))
  (p.to_outer_measure_mono (h ▸ (set.inter_subset_left s p.support)))

@[simp]
lemma to_outer_measure_apply_fintype [fintype α] : p.to_outer_measure s = ∑ x, s.indicator p x :=
(p.to_outer_measure_apply s).trans (tsum_eq_sum (λ x h, absurd (finset.mem_univ x) h))

end outer_measure

section measure

open measure_theory

/-- Since every set is Carathéodory-measurable under `pmf.to_outer_measure`,
  we can further extend this `outer_measure` to a `measure` on `α` -/
def to_measure [measurable_space α] (p : pmf α) : measure α :=
p.to_outer_measure.to_measure ((to_outer_measure_caratheodory p).symm ▸ le_top)

variables [measurable_space α] (p : pmf α) (s t : set α)

lemma to_outer_measure_apply_le_to_measure_apply : p.to_outer_measure s ≤ p.to_measure s :=
le_to_measure_apply p.to_outer_measure _ s

lemma to_measure_apply_eq_to_outer_measure_apply (hs : measurable_set s) :
  p.to_measure s = p.to_outer_measure s :=
to_measure_apply p.to_outer_measure _ hs

lemma to_measure_apply (hs : measurable_set s) : p.to_measure s = ∑' x, s.indicator p x :=
(p.to_measure_apply_eq_to_outer_measure_apply s hs).trans (p.to_outer_measure_apply s)

lemma to_measure_apply_singleton (a : α) (h : measurable_set ({a} : set α)) :
  p.to_measure {a} = p a :=
by simp [to_measure_apply_eq_to_outer_measure_apply _ _ h, to_outer_measure_apply_singleton]

lemma to_measure_apply_eq_zero_iff (hs : measurable_set s) :
  p.to_measure s = 0 ↔ disjoint p.support s :=
by rw [to_measure_apply_eq_to_outer_measure_apply p s hs,
  to_outer_measure_apply_eq_zero_iff]

lemma to_measure_apply_eq_one_iff (hs : measurable_set s) : p.to_measure s = 1 ↔ p.support ⊆ s :=
(p.to_measure_apply_eq_to_outer_measure_apply s hs : p.to_measure s = p.to_outer_measure s).symm
  ▸ (p.to_outer_measure_apply_eq_one_iff s)

@[simp]
lemma to_measure_apply_inter_support (hs : measurable_set s) (hp : measurable_set p.support) :
  p.to_measure (s ∩ p.support) = p.to_measure s :=
by simp [p.to_measure_apply_eq_to_outer_measure_apply s hs,
  p.to_measure_apply_eq_to_outer_measure_apply _ (hs.inter hp)]

lemma to_measure_mono {s t : set α} (hs : measurable_set s) (ht : measurable_set t)
  (h : s ∩ p.support ⊆ t) : p.to_measure s ≤ p.to_measure t :=
by simpa only [p.to_measure_apply_eq_to_outer_measure_apply, hs, ht]
  using to_outer_measure_mono p h

lemma to_measure_apply_eq_of_inter_support_eq {s t : set α} (hs : measurable_set s)
  (ht : measurable_set t) (h : s ∩ p.support = t ∩ p.support) : p.to_measure s = p.to_measure t :=
by simpa only [p.to_measure_apply_eq_to_outer_measure_apply, hs, ht]
  using to_outer_measure_apply_eq_of_inter_support_eq p h

section measurable_singleton_class

variables [measurable_singleton_class α]

lemma to_measure_injective : (to_measure : pmf α → measure α).injective :=
λ p q h, pmf.ext (λ x, (p.to_measure_apply_singleton x $ measurable_set_singleton x).symm.trans
  ((congr_fun (congr_arg _ h) _).trans $ q.to_measure_apply_singleton x $
    measurable_set_singleton x))

@[simp] lemma to_measure_inj {p q : pmf α} : p.to_measure = q.to_measure ↔ p = q :=
to_measure_injective.eq_iff

@[simp]
lemma to_measure_apply_finset (s : finset α) : p.to_measure s = ∑ x in s, p x :=
(p.to_measure_apply_eq_to_outer_measure_apply s s.measurable_set).trans
  (p.to_outer_measure_apply_finset s)

lemma to_measure_apply_of_finite (hs : s.finite) : p.to_measure s = ∑' x, s.indicator p x :=
(p.to_measure_apply_eq_to_outer_measure_apply s hs.measurable_set).trans
  (p.to_outer_measure_apply s)

@[simp]
lemma to_measure_apply_fintype [fintype α] : p.to_measure s = ∑ x, s.indicator p x :=
(p.to_measure_apply_eq_to_outer_measure_apply s s.to_finite.measurable_set).trans
  (p.to_outer_measure_apply_fintype s)

end measurable_singleton_class

end measure

end pmf

namespace measure_theory

open pmf

namespace measure

/-- Given that `α` is a countable, measurable space with all singleton sets measurable,
we can convert any probability measure into a `pmf`, where the mass of a point
is the measure of the singleton set under the original measure. -/
def to_pmf [countable α] [measurable_space α] [measurable_singleton_class α]
  (μ : measure α) [h : is_probability_measure μ] : pmf α :=
⟨λ x, μ ({x} : set α), ennreal.summable.has_sum_iff.2 (trans (symm $
(tsum_indicator_apply_singleton μ set.univ measurable_set.univ).symm.trans
  (tsum_congr (λ x, congr_fun (set.indicator_univ _) x))) (h.measure_univ))⟩

variables [countable α] [measurable_space α] [measurable_singleton_class α]
  (μ : measure α) [is_probability_measure μ]

lemma to_pmf_apply (x : α) : μ.to_pmf x = μ {x} := rfl

@[simp] lemma to_pmf_to_measure : μ.to_pmf.to_measure = μ :=
measure.ext (λ s hs, by simpa only [μ.to_pmf.to_measure_apply s hs,
  ← μ.tsum_indicator_apply_singleton s hs])

end measure

end measure_theory

namespace pmf

open measure_theory

/-- The measure associated to a `pmf` by `to_measure` is a probability measure -/
instance to_measure.is_probability_measure [measurable_space α] (p : pmf α) :
  is_probability_measure (p.to_measure) :=
⟨by simpa only [measurable_set.univ, to_measure_apply_eq_to_outer_measure_apply,
  set.indicator_univ, to_outer_measure_apply, ennreal.coe_eq_one] using tsum_coe p⟩

variables [countable α] [measurable_space α] [measurable_singleton_class α]
  (p : pmf α) (μ : measure α) [is_probability_measure μ]

@[simp] lemma to_measure_to_pmf : p.to_measure.to_pmf = p :=
pmf.ext (λ x, by rw [← p.to_measure_apply_singleton x (measurable_set_singleton x),
  p.to_measure.to_pmf_apply])

lemma to_measure_eq_iff_eq_to_pmf (μ : measure α) [is_probability_measure μ] :
  p.to_measure = μ ↔ p = μ.to_pmf :=
by rw [← to_measure_inj, measure.to_pmf_to_measure]

lemma to_pmf_eq_iff_to_measure_eq (μ : measure α) [is_probability_measure μ] :
  μ.to_pmf = p ↔ μ = p.to_measure :=
by rw [← to_measure_inj, measure.to_pmf_to_measure]

end pmf


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import measure_theory.measure.lebesgue

/-!
# Lebesgue measure on `ℂ`

In this file we define Lebesgue measure on `ℂ`. Since `ℂ` is defined as a `structure` as the
push-forward of the volume on `ℝ²` under the natural isomorphism. There are (at least) two
frequently used ways to represent `ℝ²` in `mathlib`: `ℝ × ℝ` and `fin 2 → ℝ`. We define measurable
equivalences (`measurable_equiv`) to both types and prove that both of them are volume preserving
(in the sense of `measure_theory.measure_preserving`).
-/

open measure_theory
noncomputable theory

namespace complex

/-- Lebesgue measure on `ℂ`. -/
instance measure_space : measure_space ℂ :=
⟨measure.map basis_one_I.equiv_fun.symm volume⟩

/-- Measurable equivalence between `ℂ` and `ℝ² = fin 2 → ℝ`. -/
def measurable_equiv_pi : ℂ ≃ᵐ (fin 2 → ℝ) :=
basis_one_I.equiv_fun.to_continuous_linear_equiv.to_homeomorph.to_measurable_equiv

/-- Measurable equivalence between `ℂ` and `ℝ × ℝ`. -/
def measurable_equiv_real_prod : ℂ ≃ᵐ (ℝ × ℝ) :=
equiv_real_prodₗ.to_homeomorph.to_measurable_equiv

lemma volume_preserving_equiv_pi :
  measure_preserving measurable_equiv_pi :=
(measurable_equiv_pi.symm.measurable.measure_preserving _).symm _

lemma volume_preserving_equiv_real_prod : measure_preserving measurable_equiv_real_prod :=
(volume_preserving_fin_two_arrow ℝ).comp volume_preserving_equiv_pi

end complex


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

! This file was ported from Lean 3 source module data.multiset.sum
! leanprover-community/mathlib commit 9003f28797c0664a49e4179487267c494477d853
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Multiset.Nodup

/-!
# Disjoint sum of multisets

This file defines the disjoint sum of two multisets as `Multiset (α ⊕ β)`. Beware not to confuse
with the `Multiset.sum` operation which computes the additive sum.

## Main declarations

* `Multiset.disjSum`: `s.disjSum t` is the disjoint sum of `s` and `t`.
-/

open Sum

namespace Multiset

variable {α β : Type _} (s : Multiset α) (t : Multiset β)

/-- Disjoint sum of multisets. -/
def disjSum : Multiset (Sum α β) :=
  s.map inl + t.map inr
#align multiset.disj_sum Multiset.disjSum

@[simp]
theorem zero_disjSum : (0 : Multiset α).disjSum t = t.map inr :=
  zero_add _
#align multiset.zero_disj_sum Multiset.zero_disjSum

@[simp]
theorem disjSum_zero : s.disjSum (0 : Multiset β) = s.map inl :=
  add_zero _
#align multiset.disj_sum_zero Multiset.disjSum_zero

@[simp]
theorem card_disjSum : Multiset.card (s.disjSum t) = Multiset.card s + Multiset.card t := by
  rw [disjSum, card_add, card_map, card_map]
#align multiset.card_disj_sum Multiset.card_disjSum

variable {s t} {s₁ s₂ : Multiset α} {t₁ t₂ : Multiset β} {a : α} {b : β} {x : Sum α β}

theorem mem_disjSum : x ∈ s.disjSum t ↔ (∃ a, a ∈ s ∧ inl a = x) ∨ ∃ b, b ∈ t ∧ inr b = x := by
  simp_rw [disjSum, mem_add, mem_map]
#align multiset.mem_disj_sum Multiset.mem_disjSum

@[simp]
theorem inl_mem_disjSum : inl a ∈ s.disjSum t ↔ a ∈ s := by
  rw [mem_disjSum, or_iff_left]
  -- Porting note: Previous code for L62 was: simp only [exists_eq_right]
  simp only [inl.injEq, exists_eq_right]
  rintro ⟨b, _, hb⟩
  exact inr_ne_inl hb
#align multiset.inl_mem_disj_sum Multiset.inl_mem_disjSum

@[simp]
theorem inr_mem_disjSum : inr b ∈ s.disjSum t ↔ b ∈ t := by
  rw [mem_disjSum, or_iff_right]
  -- Porting note: Previous code for L72 was: simp only [exists_eq_right]
  simp only [inr.injEq, exists_eq_right]
  rintro ⟨a, _, ha⟩
  exact inl_ne_inr ha
#align multiset.inr_mem_disj_sum Multiset.inr_mem_disjSum

theorem disjSum_mono (hs : s₁ ≤ s₂) (ht : t₁ ≤ t₂) : s₁.disjSum t₁ ≤ s₂.disjSum t₂ :=
  add_le_add (map_le_map hs) (map_le_map ht)
#align multiset.disj_sum_mono Multiset.disjSum_mono

theorem disjSum_mono_left (t : Multiset β) : Monotone fun s : Multiset α => s.disjSum t :=
  fun _ _ hs => add_le_add_right (map_le_map hs) _
#align multiset.disj_sum_mono_left Multiset.disjSum_mono_left

theorem disjSum_mono_right (s : Multiset α) :
    Monotone (s.disjSum : Multiset β → Multiset (Sum α β)) := fun _ _ ht =>
  add_le_add_left (map_le_map ht) _
#align multiset.disj_sum_mono_right Multiset.disjSum_mono_right

theorem disjSum_lt_disjSum_of_lt_of_le (hs : s₁ < s₂) (ht : t₁ ≤ t₂) :
    s₁.disjSum t₁ < s₂.disjSum t₂ :=
  add_lt_add_of_lt_of_le (map_lt_map hs) (map_le_map ht)
#align multiset.disj_sum_lt_disj_sum_of_lt_of_le Multiset.disjSum_lt_disjSum_of_lt_of_le

theorem disjSum_lt_disjSum_of_le_of_lt (hs : s₁ ≤ s₂) (ht : t₁ < t₂) :
    s₁.disjSum t₁ < s₂.disjSum t₂ :=
  add_lt_add_of_le_of_lt (map_le_map hs) (map_lt_map ht)
#align multiset.disj_sum_lt_disj_sum_of_le_of_lt Multiset.disjSum_lt_disjSum_of_le_of_lt

theorem disjSum_strictMono_left (t : Multiset β) : StrictMono fun s : Multiset α => s.disjSum t :=
  fun _ _ hs => disjSum_lt_disjSum_of_lt_of_le hs le_rfl
#align multiset.disj_sum_strict_mono_left Multiset.disjSum_strictMono_left

theorem disjSum_strictMono_right (s : Multiset α) :
    StrictMono (s.disjSum : Multiset β → Multiset (Sum α β)) := fun _ _ =>
  disjSum_lt_disjSum_of_le_of_lt le_rfl
#align multiset.disj_sum_strict_mono_right Multiset.disjSum_strictMono_right

protected theorem Nodup.disjSum (hs : s.Nodup) (ht : t.Nodup) : (s.disjSum t).Nodup := by
  refine' ((hs.map inl_injective).add_iff <| ht.map inr_injective).2 fun x hs ht => _
  rw [Multiset.mem_map] at hs ht
  obtain ⟨a, _, rfl⟩ := hs
  obtain ⟨b, _, h⟩ := ht
  exact inr_ne_inl h
#align multiset.nodup.disj_sum Multiset.Nodup.disjSum

end Multiset


import combinatorics.simple_graph.clique
import combinatorics.simple_graph.degree_sum
import data.finset.basic
import data.nat.basic
import tactic.core
import algebra.big_operators

--local
import turanpartition
import multipartite
import nbhd_res
import fedges

open finset nat turanpartition
open_locale big_operators
namespace simple_graph
section ind
variables {t n : ℕ}
variables {α : Type*} (G H : simple_graph α)[fintype α][nonempty α]{s : finset α}
[decidable_eq α][decidable_rel G.adj][decidable_rel H.adj]

-- I found dealing with the mathlib "induced" subgraph too painful (probably just too early in my experience of lean)

-- Graph induced by A:finset α, defined to be a simple_graph α (so all vertices outside A have empty neighborhoods)
-- this is equvialent to  "spanning_coe (induce (A:set α) G)" as we prove below.

@[ext,reducible]
def ind (A : finset α) : simple_graph α :={
  adj:= λ x y, G.adj x y ∧ x ∈ A ∧ y ∈ A,
  symm:=
  begin
    intros x y hxy, rw adj_comm, tauto,
  end,
  loopless:= by obviously}

lemma ind_univ : G.ind univ = G :=
begin
  ext, simp only [mem_univ, and_true],
end

-- why is this so messy to prove? (presumably it isn't..)
lemma ind_eq_coe_induced {A : finset α} : spanning_coe (induce (A:set α) G) = (G.ind A):=
begin
  ext, simp only [map_adj, comap_adj, function.embedding.coe_subtype, set_coe.exists, mem_coe, subtype.coe_mk, exists_prop],
  split, {rintros ⟨a,h1,b,h2,h3,h4,h5⟩,rw [←h4,←h5], exact ⟨h3,h1,h2⟩},
  {rintros ⟨h,h1,h2⟩, exact ⟨x,h1,x_1,h2,h,rfl,rfl⟩,},
end

-- induced subgraphs on disjoint sets meet in the empty graph
lemma empty_of_disjoint_ind {A B: finset α} (h : disjoint A B): G.ind A ⊓ G.ind B = ⊥ :=
begin
ext , simp only [inf_adj, bot_adj], split, {rintro ⟨⟨_,h1,_⟩,⟨_,h2,_⟩⟩, exact h (mem_inter.mpr ⟨h1,h2⟩)},
{tauto},
end

lemma empty_of_ind_comp (A : finset α) : G.ind A ⊓ G.ind Aᶜ =⊥:=
begin
  have :disjoint A Aᶜ:=disjoint_compl_right, exact G.empty_of_disjoint_ind (this),
end

-- different parts of a multi_part induce graphs that meet in the empty graph
lemma empty_of_diff_parts {M : multi_part α} {i j : ℕ}(hi: i∈range(M.t+1)) (hj: j∈range(M.t+1)) (hne:i≠j):
G.ind (M.P i) ⊓ G.ind (M.P j) = ⊥ := G.empty_of_disjoint_ind (M.disj i hi j hj hne)

-- would like to just define the bUnion of the induced graphs directly but can't figure out how to do this.
@[ext]
def edges_inside (M : multi_part α) : finset(sym2 α):=(range(M.t+1)).bUnion (λi, (G.ind (M.P i)).edge_finset)

--so counting edges inside M is same as summing of edges in induced parts (since parts are disjoint..)
lemma edge_mp_count {M : multi_part α} : (G.edges_inside M).card = ∑ i in range(M.t+1),(G.ind (M.P i)).edge_finset.card:=
begin
 apply card_bUnion, intros i hi j hj hne, rw disjoint_edges_iff_meet_empty,exact G.empty_of_diff_parts hi hj hne,
end

-- if v w are adjacent in induced graph then they are adjacent in G
lemma ind_adj_imp {A :finset α} {v w :α} : (G.ind A).adj v w → G.adj v w:=λ h, h.1

-- if v w are adjacent in induced graph on A then they are both in A
lemma ind_adj_imp' {A :finset α} {v w :α} : (G.ind A).adj v w → v ∈ A ∧ w ∈ A:=λ h , h.2

--nbhd of v ∈ A in the graph induced by A is exactly the nbhd of v restricted to A
lemma ind_nbhd_mem {A : finset α} {v : α} : v∈ A → (G.ind A).neighbor_finset v =  G.nbhd_res v A:=
begin
  intros hv,unfold neighbor_finset nbhd_res, ext,
  simp only [*, set.mem_to_finset, mem_neighbor_set, and_self] at *,
  split,{intro ha, rw [mem_inter,set.mem_to_finset,mem_neighbor_set],exact ⟨ha.2.2,ha.1⟩},
  {rw [mem_inter, set.mem_to_finset, mem_neighbor_set], tauto},
end

-- induced degree of  v ∈ A is deg res to A
lemma ind_deg_mem {A : finset α} {v : α} : v∈ A → (G.ind A).degree v= G.deg_res v A:=
begin
  unfold degree deg_res,intros hv, congr,exact G.ind_nbhd_mem hv,
end

-- if v∉A then v has no neighbors in the induced graph G.ind A
lemma ind_nbhd_nmem {A : finset α} {v : α} : v∉A → ((G.ind A).neighbor_finset v) = ∅:=
begin
  contrapose,  push_neg,intros h, obtain ⟨w,hw⟩:=nonempty.bex (nonempty_of_ne_empty h),
  rw mem_neighbor_finset at hw, exact hw.2.1,
end

-- if v∉ A then (G.ind A).degree v is zero
lemma ind_deg_nmem {A : finset α} {v : α} : v∉A → (G.ind A).degree v=0:=
λ h, card_eq_zero.mpr (G.ind_nbhd_nmem h)

-- so degrees of v in the induced graph are deg_res v A or 0 depending on whether or not v ∈ A
lemma ind_deg {A :finset α}{v:α} : (G.ind A).degree v = ite (v∈A) (G.deg_res v A) (0):=
begin
  unfold degree,
  split_ifs,{unfold deg_res,congr, exact G.ind_nbhd_mem h},
  {rw G.ind_nbhd_nmem h, apply card_eq_zero.mpr rfl},
end

-- finite support of G
def fsupport : finset α := univ.filter (λ v, 0 < G.degree v )

-- member of support iff degree >0
lemma mem_fsupport (v : α) : v ∈ G.fsupport ↔ 0 < G.degree v:=
begin
  unfold fsupport,rw mem_filter, simp only [mem_univ, true_and],
end

lemma deg_fsupport (v : α) : G.degree v = ite (v∈ G.fsupport) (G.degree v) (0):=
begin
  split_ifs, refl,rw mem_fsupport at h, linarith,
end

-- a vertex is in the fsupport of (G.ind A) only if it is in A
lemma mem_ind_fsupport {v : α} {A: finset α} : v ∈ (G.ind A).fsupport → v ∈ A:=
begin
  rw (G.ind A).mem_fsupport v ,contrapose , push_neg, intros hv, rw G.ind_deg_nmem hv,
end

-- so when calculating any sum of degrees over a set can restrict to fsupport
lemma deg_sum_fsupport {A : finset α} : ∑ v in A, G.degree v = ∑ v in A.filter (λ v , v ∈ G.fsupport), G.degree v:=
begin
  rw sum_filter, apply sum_congr rfl, intros x hx,exact G.deg_fsupport x,
end

-- so degree sum over α in the induced subgraph is same as sum of deg_res over A
-- both count 2*e(G[A])
lemma ind_deg_sum {A :finset α}: ∑v, (G.ind A).degree v = ∑v in A,(G.deg_res v A):=
begin
  simp only [ind_deg], rw sum_ite,rw sum_const, rw smul_zero,rw add_zero, congr,
  ext,rw mem_filter,simp only [mem_univ],tauto,
end

--induced subgraph is a subgraph
lemma ind_sub (A : finset α) : (G.ind A)≤ G:=  λ x y, G.ind_adj_imp

-- internal edges induced by parts of a partition M
-- I should have defined this as G \(⋃ (G.ind M.P i)) if I
-- could have made it work.. defining the bUnion operator for simple_graphs
-- was a step too far..

---bipartite graph induced by a finset A
@[ext,reducible]
def bipart (A :finset α) :simple_graph α :=
{ adj:= λ v w, (G.adj v w) ∧ (v∈ A ∧ w ∉ A ∨ v∉ A ∧ w ∈ A),
  symm :=begin intros x y hxy, rw adj_comm, tauto, end,
  loopless :=by obviously,}

-- the bipartite graph induced by A (and Aᶜ) is the same as that induced by Aᶜ (and A = Aᶜᶜ)
lemma bipart_comp_eq_self {A : finset α} : (G.bipart A)=(G.bipart Aᶜ):=
begin
  tidy; tauto
end

--induced subgraph on A meets bipartite induced subgraph e(A,Aᶜ) in empty graph
lemma empty_of_ind_bipart (A: finset α) : (G.ind A ⊔ G.ind Aᶜ) ⊓ G.bipart A = ⊥ :=
begin
  ext, simp only [inf_adj, sup_adj,bot_adj, mem_compl], tauto,
end

-- Given A:finset α and G :simple_graph α we can partition G into G[A] G[Aᶜ] and G[A,Aᶜ]
lemma split_induced (A : finset α): G = (G.ind A ⊔ G.ind Aᶜ) ⊔  G.bipart A:=
begin
  ext, simp only [sup_adj, mem_compl], tauto,
end

--- Edge counting: e(G[A])+e(G[Aᶜ])+e(G[A,Aᶜ])=e(G)
lemma edges_split_induced  (A : finset α):  (G.ind A).edge_finset.card + (G.ind Aᶜ).edge_finset.card
+ (G.bipart A).edge_finset.card = G.edge_finset.card :=
begin
  have:=G.split_induced A,
  rw eq_iff_edges_eq at this, rw this,
  rw card_edges_add_of_meet_empty (G.empty_of_ind_bipart A), rw add_left_inj,
  rwa card_edges_add_of_meet_empty (G.empty_of_ind_comp A),
end

-- v w adjacent in the bipartite graph given by A iff adj in bipartite graph given by Aᶜ (since they are the same graph..)
lemma bipart_comp_adj_iff {A : finset α} {v w :α} : (G.bipart A).adj v w ↔ (G.bipart Aᶜ).adj v w:=
begin
  tidy; tauto,
end

-- nbhd of v ∈ A in the bipartite graph is the nbhd of v in G restricted to Aᶜ
lemma nbhd_bipart_mem {A : finset α} {v : α} (h: v∈ A) : (G.bipart A).neighbor_finset v = G.nbhd_res v Aᶜ:=
begin
  ext, rw [mem_res_nbhd, mem_neighbor_finset,mem_neighbor_finset], tidy; tauto,
end

-- hence degree is deg_res v to Aᶜ
lemma deg_bipart_mem {A : finset α} {v : α} (h: v∈ A) : (G.bipart A).degree v = (G.deg_res v Aᶜ):=
begin
  unfold degree deg_res,
  rwa nbhd_bipart_mem,
end

-- nbhd of v ∉ A in the bipartite graph is the nbhd of v in G restricted to A
lemma nbhd_bipart_not_mem {A : finset α} {v : α} (h: v ∉ A) :  (G.bipart A).neighbor_finset v = G.nbhd_res v A:=
begin
  ext, simp only [mem_res_nbhd, mem_neighbor_finset], tidy; tauto,
end

-- if v∉ A then in the bipartite graph deg v is the deg_res to A
lemma deg_bipart_not_mem {A : finset α} {v : α} (h: v ∉ A) :  (G.bipart A).degree v = G.deg_res v A:=
begin
  unfold degree deg_res,rwa nbhd_bipart_not_mem,
end

-- degree of v ∈ A is degree in induced + degree in bipartite (ie count neighbour in A and Aᶜ)
lemma deg_eq_ind_add_bipart {A : finset α} {v : α} : ∀v∈A,  G.degree v= (G.ind A).degree v + (G.bipart A).degree v:=
begin
  intros v hv,  rw G.deg_bipart_mem hv, rw G.ind_deg_mem hv, rw ← G.deg_res_univ,
  exact G.deg_res_add (subset_univ A),
end

--ite to count edges from A to Aᶜ
lemma bipart_sum_ite {A : finset α}: ∑ v in A, (G.bipart A).degree v = ∑ v in A, ∑  w in Aᶜ, ite (G.adj v w) (1) (0):=
begin
  apply sum_congr rfl, intros x hx, rw G.deg_bipart_mem hx, rw deg_res,rw nbhd_res,
  rw card_eq_sum_ones, simp only [*, sum_const, algebra.id.smul_eq_mul, mul_one, sum_boole, cast_id],
  congr,ext,rw mem_inter,rw mem_neighbor_finset,rw mem_filter,
end

-- sum of degrees over each part are equal in any induced bipartite graph
lemma bipart_sum_eq {A : finset α}: ∑ v in A, (G.bipart A).degree v = ∑ v in Aᶜ, (G.bipart A).degree v :=
begin
  rw [G.bipart_sum_ite, sum_comm], apply sum_congr rfl, intros x hx, rw [compl_eq_univ_sdiff, mem_sdiff] at hx,
  rw [G.deg_bipart_not_mem hx.2,  deg_res, nbhd_res, card_eq_sum_ones, sum_ite, sum_const,sum_const],
  dsimp, rw [mul_one,  mul_zero,  add_zero, card_eq_sum_ones], apply sum_congr, ext,
  rw [mem_inter,  mem_filter, mem_neighbor_finset,  adj_comm],
  intros y hy,refl,
end

-- hence sum of degrees over one part counts edges once
lemma sum_deg_bipart_eq_edge_card {A : finset α} :∑ v in A, (G.bipart A).degree v = (G.bipart A).edge_finset.card :=
begin
  apply (nat.mul_right_inj (by norm_num:0<2)).mp, rw ← sum_degrees_eq_twice_card_edges,
  rw [(by norm_num:2=1+1), add_mul, one_mul], nth_rewrite 0 G.bipart_sum_eq,
  rw compl_eq_univ_sdiff, symmetry,
  have:disjoint (univ\A) A := sdiff_disjoint,
  rw ← sum_union this, rw sdiff_union_of_subset (subset_univ A),
end

--- in the induced graph only need to sum over A to count all edges twice
lemma sum_degrees_ind_eq_twice_card_edges {A : finset α} : ∑v in A, (G.ind A).degree v = 2*(G.ind A).edge_finset.card:=
begin
  rw ← sum_degrees_eq_twice_card_edges, rw  ind_deg_sum, apply sum_congr rfl,
  intros x hx, exact G.ind_deg_mem hx,
end

-- sum of degrees in A = twice number of edges in A + number of edges from A to Aᶜ
lemma sum_deg_ind_bipart {A : finset α} : ∑ v in A, G.degree v = 2*(G.ind A).edge_finset.card + (G.bipart A).edge_finset.card :=
begin
  rw ← sum_degrees_ind_eq_twice_card_edges, rw ← sum_deg_bipart_eq_edge_card, rw ← sum_add_distrib,
  apply sum_congr rfl,intros x hx, rwa G.deg_eq_ind_add_bipart x hx,
end

-- any nbhd is contained in the fsupport
lemma nbhd_sub_fsupport (v : α) :G.neighbor_finset v ⊆ G.fsupport :=
begin
  intro x,rw [mem_neighbor_finset, mem_fsupport, degree, card_pos],
  intro h, rw [adj_comm, ← mem_neighbor_finset] at h, exact ⟨v,h⟩,
end

-- should have been one line (on finsets not graphs) but couldn't find it: A ⊆ B → Aᶜ ∩ B = B\A
lemma comp_nbhd_int_supp_eq_sdiff (v : α) :(G.neighbor_finset v)ᶜ ∩  G.fsupport = G.fsupport \(G.neighbor_finset v):=
begin
  have h:=G.nbhd_sub_fsupport v, rw [sdiff_eq,  inter_comm], refl,
end

-- Bound on max degree gives bound on edges of G in the following form:
--(Note this almost gives Mantel's theorem since in a K_3-free graph nbhds are independent)
lemma sum_deg_ind_max_nbhd {v : α} {A : finset α} (hm: G.degree v= G.max_degree) (hA: A=(G.neighbor_finset v)ᶜ) :
 2*(G.ind A).edge_finset.card + (G.bipart A).edge_finset.card ≤   (G.fsupport.card - G.max_degree)*G.max_degree:=
begin
  rw [← G.sum_deg_ind_bipart, G.deg_sum_fsupport, ← hm,  degree],
  rw [sum_filter, sum_ite, sum_const, smul_zero, add_zero, ← card_sdiff (G.nbhd_sub_fsupport v)],
  nth_rewrite 0 card_eq_sum_ones,rw [sum_mul, one_mul],
  rw hA, rw [filter_mem_eq_inter,  G.comp_nbhd_int_supp_eq_sdiff v],
  apply sum_le_sum, rw [← degree, hm], intros x hx, exact G.degree_le_max_degree x,
end

-- The essential bound for Furedi's result:
-- e(G) + e(G[Γ(v)ᶜ]) ≤ (G.fsupport.card - Δ(G))*Δ(G) + e(G[Γ(v)])
lemma edge_bound_max_deg {v : α} {A : finset α} (hm: G.degree v= G.max_degree) (hA: A=(G.neighbor_finset v)ᶜ) :
 G.edge_finset.card + (G.ind A).edge_finset.card ≤
 (G.fsupport.card - G.max_degree)*G.max_degree + (G.ind Aᶜ).edge_finset.card:=
begin
  rw ← G.edges_split_induced A, have:=G.sum_deg_ind_max_nbhd hm hA, linarith,
end

-- any "actual" clique consists of vertices in the support
lemma clique_sub_fsupport {t : ℕ} {S :finset α} (ht: 2 ≤ t) (h: G.is_n_clique t S) : S ⊆ G.fsupport:=
begin
  intros a ha, rw is_n_clique_iff at h,  have :(1 < S.card):= by linarith,
  obtain ⟨b,hb,hne⟩:= exists_ne_of_one_lt_card this a,
  rw ← mem_coe at *, have hadj:=h.1 hb ha hne, rw mem_coe, rw ← mem_neighbor_finset at hadj,
  exact (G.nbhd_sub_fsupport b) hadj,
end

-- any t+2 clique of an induced graph is a subset of the induced set
lemma clique_ind_sub_ind {A S : finset α} (h: 2 ≤ t): (G.ind A).is_n_clique t S → S ⊆ A:=
begin
  intros h1 a ha, exact G.mem_ind_fsupport (((G.ind A).clique_sub_fsupport h h1) ha),
end

-- if S a t-clique in a nbhd Γ(v) then inserting v gives a (t+1)-clique
lemma clique_insert_nbhr {t : ℕ} {S :finset α} {v : α} (hc: G.is_n_clique t S) (hd: S ⊆ G.neighbor_finset v) :
 G.is_n_clique (t+1) (insert v S):=
begin
   rw is_n_clique_iff at *, rw ← hc.2, have vnin:v∉S:=by apply set.not_mem_subset hd (G.not_mem_nbhd v),
   rw is_clique_iff at *, refine ⟨_,card_insert_of_not_mem vnin⟩, rw coe_insert,
   refine set.pairwise.insert hc.1 _, intros b hb hj, rw [G.adj_comm b v, and_self,← mem_neighbor_finset], exact hd hb,
end

-- Now the other key lemma for Furedi's result:
-- If G is K_{t+3}-free then any nbhd induces a K_{t+2}-free graph
-- (Could replace t by t-1 below, since G K_2 free → nbhds all empty → graphs induced by nbhds empty → they are K_1 -free )
lemma clique_free_nbhd_ind {t : ℕ} {v : α} : G.clique_free (t+3) →  (G.ind (G.neighbor_finset v)).clique_free (t+2):=
begin
  contrapose, unfold clique_free, push_neg, rintro ⟨S,hs⟩, use (insert v S),
  have:=G.clique_insert_nbhr (⟨(is_clique.mono (G.ind_sub (G.neighbor_finset v)) hs.1),hs.2⟩) (G.clique_ind_sub_ind (by linarith) hs),
  rwa [(by norm_num: 3=2+1),← add_assoc],
end

@[ext,reducible]
def bUnion (M: multi_part α) : simple_graph α := {
adj:= λ v w, ∃i ∈ range(M.t+1), (G.ind (M.P i)).adj v w,
symm:= by obviously,
loopless:= by obviously,}

@[ext,reducible]
def disJoin (M: multi_part α) : simple_graph α:={
adj:= λ v w , (G.adj v w) ∧ (∃ i ∈ range(M.t+1), v∈(M.P i) ∧w∈ (M.P i)),
symm:= by obviously,
loopless:= by obviously,}

-- the two versions of "union of induced disjoint parts" are the same
lemma bUnion_eq_disJoin_sum (M : multi_part α) : G.bUnion M = G.disJoin M:=
begin
  ext,simp only [mem_range, exists_prop],split,
  {rintros ⟨i,hi,ad,hx,hy⟩,exact ⟨ad,i,hi,hx,hy⟩},{rintros ⟨ad,i,hi,hx,hy⟩,exact ⟨i,hi,ad,hx,hy⟩},
end

-- edges inside M are the same as the edge_finset of bUnion M
lemma edges_inside_eq (M : multi_part α) : (G.bUnion M).edge_finset = (G.edges_inside M):=
begin
  unfold edges_inside, ext, simp only [mem_edge_finset, mem_bUnion, mem_range, exists_prop],
  unfold bUnion, induction a, work_on_goal 1 { cases a, dsimp at *, simp only [mem_range, exists_prop] at *, refl }, refl,
end

-- this is a subgraph of G
lemma disJoin_sub (M : multi_part α) : (G.disJoin M)≤ G:=λ _ _ h, h.1

-- G with the internal edges removed is G ⊓ (mp M)
lemma sdiff_with_int {M: multi_part α} (h: M.A =univ)  : G\(G.disJoin M) = G⊓(mp M):=
begin
  ext x y,dsimp,
  have hx: x∈ M.A:=by {rw h, exact mem_univ x},
  have hy: y∈ M.A:=by {rw h, exact mem_univ y},
  obtain ⟨i,hi,hx1⟩:=inv_part hx,
  obtain ⟨j,hj,hy1⟩:=inv_part hy,
  split,{
    rintros ⟨hadj,h2⟩,refine ⟨hadj,_⟩, push_neg at h2, have h3:=h2 hadj,
    specialize h3 i hi hx1,
    refine mp_imp_adj hi hj hx1 hy1 _,
    intro ne,rw ne at h3, exact h3 hy1,},{
    rintros ⟨hadj,h2⟩, refine ⟨hadj,_⟩, push_neg, intros hadj' i hi hx hy,
    exact not_nbhr_same_part hi hx h2 hy },
end

-- G is the join of the edges induced by the parts and those in the complete
-- multipartite graph M on α
lemma self_eq_disJoin_ext_mp {M :multi_part α} (h: M.A=univ) : G = (G.disJoin M) ⊔ (G⊓(mp M)):=
begin
  rw ← G.sdiff_with_int h,simp only [sup_sdiff_self_right, right_eq_sup], exact G.disJoin_sub M,
end

-- Given M and v,w vertices with v ∈ M.P i and w ∈ M.A then v,w are adjacent in
-- the "internal induced subgraph"  iff they are adjacent in the graph induced on M.P i
lemma disJoin_edge_help {M :multi_part α} {v w : α} {i : ℕ}: i ∈ range(M.t+1) → v ∈ (M.P i) →  w ∈ M.A →
((G.disJoin M).adj v w ↔ (G.ind (M.P i)).adj v w):=
begin
  intros hi hv hw, obtain ⟨j,hj,hw⟩:=inv_part hw,dsimp,
  by_cases i=j,{
      split, {intros h1,rw ←h at hw,exact ⟨h1.1,hv,hw⟩},
      {intros h1, cases h1, exact ⟨h1_left,i,hi,h1_right⟩},},
  { split,{intros h1, cases h1 with h2 h3, exfalso, rcases h3 with ⟨k, hkr, hk⟩,
  have ieqk:=uniq_part hi hkr hv hk.1,have jeqk:=uniq_part hj hkr hw hk.2,
  rw ← jeqk at ieqk, exact h ieqk},
  {intros h1, exfalso, exact uniq_part' hi hj h h1.2.2 hw}, },
end

--same as above but for degrees and assuming M covers all of α
lemma disJoin_edge_help' {M :multi_part α} (h: M.A=univ) {v w:α}{i:ℕ}: i∈ range(M.t+1) → v ∈ (M.P i) →
(G.disJoin M).degree v = (G.ind (M.P i)).degree v:=
begin
  intros hi hv, unfold degree, apply congr_arg _, ext,
  have :=mem_univ a,rw ← h at this, have:=G.disJoin_edge_help  hi hv this,
  rwa [mem_neighbor_finset,mem_neighbor_finset],
end

-- so sum of degrees in internal subgraph is sum over induced subgraphs on parts of sum of degrees
lemma disJoin_deg_sum {M :multi_part α} (h: M.A=univ) :
 ∑v,(G.disJoin M).degree v =  ∑  i in range(M.t+1), ∑ v, (G.ind (M.P i)).degree v:=
begin
have :=bUnion_parts M, rw ← h,nth_rewrite 0 this,
 rw sum_bUnion (pair_disjoint M),
 refine sum_congr rfl _,
 intros i hi, rw (sdiff_part hi), rw sum_union (sdiff_disjoint),
 have :∑x in M.A\(M.P i),(G.ind (M.P i)).degree x =0,{
  apply sum_eq_zero,intros x hx, rw mem_sdiff at hx, exact G.ind_deg_nmem hx.2,},
  rw this,rw zero_add, apply sum_congr rfl _, intros x hx, apply (G.disJoin_edge_help' h) hi hx, exact x,
end

-- number of edges in the subgraph induced inside all parts is the sum of those induced in each part
lemma disJoin_edge_sum {M :multi_part α} (h: M.A=univ) :
(G.disJoin M).edge_finset.card =  ∑  i in range(M.t+1), (G.ind (M.P i)).edge_finset.card:=
begin
  apply (nat.mul_right_inj (by norm_num:0<2)).mp, rw mul_sum,
  simp only [← sum_degrees_eq_twice_card_edges], exact G.disJoin_deg_sum h,
end

--counting edges in induced parts is (almost) the same as summing restricted degrees...
lemma ind_edge_count {A : finset α}: ∑  v in A, G.deg_res v A = 2* ((G.ind A).edge_finset.card ) :=
begin
  rw [← sum_degrees_eq_twice_card_edges,G.ind_deg_sum],
end

end ind

end simple_graph


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

universes v u₁ u₂

namespace Mathlib

/-!
# Relating monomorphisms and epimorphisms to limits and colimits

If `F` preserves (resp. reflects) pullbacks, then it preserves (resp. reflects) monomorphisms.

## TODO

Dualise and apply to functor categories.

-/

namespace category_theory

/-- If `F` preserves pullbacks, then it preserves monomorphisms. -/
protected instance preserves_mono {C : Type u₁} {D : Type u₂} [category C] [category D] (F : C ⥤ D) {X : C} {Y : C} (f : X ⟶ Y) [limits.preserves_limit (limits.cospan f f) F] [mono f] : mono (functor.map F f) := sorry

/-- If `F` reflects pullbacks, then it reflects monomorphisms. -/
theorem reflects_mono {C : Type u₁} {D : Type u₂} [category C] [category D] (F : C ⥤ D) {X : C} {Y : C} (f : X ⟶ Y) [limits.reflects_limit (limits.cospan f f) F] [mono (functor.map F f)] : mono f := sorry


/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Yaël Dillies

! This file was ported from Lean 3 source module data.finset.mul_antidiagonal
! leanprover-community/mathlib commit 34ee86e6a59d911a8e4f89b68793ee7577ae79c7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Pointwise.Basic
import Mathbin.Data.Set.MulAntidiagonal

/-! # Multiplication antidiagonal as a `finset`.

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

We construct the `finset` of all pairs
of an element in `s` and an element in `t` that multiply to `a`,
given that `s` and `t` are well-ordered.-/

namespace Set

open Pointwise

variable {α : Type _} {s t : Set α}

/- warning: set.is_pwo.mul -> Set.IsPwo.mul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} α} {t : Set.{u1} α} [_inst_1 : OrderedCancelCommMonoid.{u1} α], (Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) s) -> (Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) t) -> (Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) (HMul.hMul.{u1, u1, u1} (Set.{u1} α) (Set.{u1} α) (Set.{u1} α) (instHMul.{u1} (Set.{u1} α) (Set.mul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α _inst_1)))))))) s t))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} α} {t : Set.{u1} α} [_inst_1 : OrderedCancelCommMonoid.{u1} α], (Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) s) -> (Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) t) -> (Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) (HMul.hMul.{u1, u1, u1} (Set.{u1} α) (Set.{u1} α) (Set.{u1} α) (instHMul.{u1} (Set.{u1} α) (Set.mul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α _inst_1)))))))) s t))
Case conversion may be inaccurate. Consider using '#align set.is_pwo.mul Set.IsPwo.mulₓ'. -/
@[to_additive]
theorem IsPwo.mul [OrderedCancelCommMonoid α] (hs : s.IsPwo) (ht : t.IsPwo) : IsPwo (s * t) :=
  by
  rw [← image_mul_prod]
  exact (hs.prod ht).image_of_monotone (monotone_fst.mul' monotone_snd)
#align set.is_pwo.mul Set.IsPwo.mul
#align set.is_pwo.add Set.IsPwo.add

variable [LinearOrderedCancelCommMonoid α]

/- warning: set.is_wf.mul -> Set.IsWf.mul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} α} {t : Set.{u1} α} [_inst_1 : LinearOrderedCancelCommMonoid.{u1} α], (Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) s) -> (Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) t) -> (Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) (HMul.hMul.{u1, u1, u1} (Set.{u1} α) (Set.{u1} α) (Set.{u1} α) (instHMul.{u1} (Set.{u1} α) (Set.mul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))))))))) s t))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} α} {t : Set.{u1} α} [_inst_1 : LinearOrderedCancelCommMonoid.{u1} α], (Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) s) -> (Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) t) -> (Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) (HMul.hMul.{u1, u1, u1} (Set.{u1} α) (Set.{u1} α) (Set.{u1} α) (instHMul.{u1} (Set.{u1} α) (Set.mul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))))))))) s t))
Case conversion may be inaccurate. Consider using '#align set.is_wf.mul Set.IsWf.mulₓ'. -/
@[to_additive]
theorem IsWf.mul (hs : s.IsWf) (ht : t.IsWf) : IsWf (s * t) :=
  (hs.IsPwo.mul ht.IsPwo).IsWf
#align set.is_wf.mul Set.IsWf.mul
#align set.is_wf.add Set.IsWf.add

/- warning: set.is_wf.min_mul -> Set.IsWf.min_mul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} α} {t : Set.{u1} α} [_inst_1 : LinearOrderedCancelCommMonoid.{u1} α] (hs : Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) s) (ht : Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) t) (hsn : Set.Nonempty.{u1} α s) (htn : Set.Nonempty.{u1} α t), Eq.{succ u1} α (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))) (HMul.hMul.{u1, u1, u1} (Set.{u1} α) (Set.{u1} α) (Set.{u1} α) (instHMul.{u1} (Set.{u1} α) (Set.mul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))))))))) s t) (Set.IsWf.mul.{u1} α s t _inst_1 hs ht) (Set.Nonempty.mul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))))))) s t hsn htn)) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))))))) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))) s hs hsn) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))) t ht htn))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} α} {t : Set.{u1} α} [_inst_1 : LinearOrderedCancelCommMonoid.{u1} α] (hs : Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) s) (ht : Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))) t) (hsn : Set.Nonempty.{u1} α s) (htn : Set.Nonempty.{u1} α t), Eq.{succ u1} α (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))) (HMul.hMul.{u1, u1, u1} (Set.{u1} α) (Set.{u1} α) (Set.{u1} α) (instHMul.{u1} (Set.{u1} α) (Set.mul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))))))))) s t) (Set.IsWf.mul.{u1} α s t _inst_1 hs ht) (Set.Nonempty.mul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))))))) s t hsn htn)) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1)))))))) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))) s hs hsn) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_1))) t ht htn))
Case conversion may be inaccurate. Consider using '#align set.is_wf.min_mul Set.IsWf.min_mulₓ'. -/
@[to_additive]
theorem IsWf.min_mul (hs : s.IsWf) (ht : t.IsWf) (hsn : s.Nonempty) (htn : t.Nonempty) :
    (hs.mul ht).min (hsn.mul htn) = hs.min hsn * ht.min htn :=
  by
  refine' le_antisymm (is_wf.min_le _ _ (mem_mul.2 ⟨_, _, hs.min_mem _, ht.min_mem _, rfl⟩)) _
  rw [is_wf.le_min_iff]
  rintro _ ⟨x, y, hx, hy, rfl⟩
  exact mul_le_mul' (hs.min_le _ hx) (ht.min_le _ hy)
#align set.is_wf.min_mul Set.IsWf.min_mul
#align set.is_wf.min_add Set.IsWf.min_add

end Set

namespace Finset

open Pointwise

variable {α : Type _}

variable [OrderedCancelCommMonoid α] {s t : Set α} (hs : s.IsPwo) (ht : t.IsPwo) (a : α)

#print Finset.mulAntidiagonal /-
/-- `finset.mul_antidiagonal_of_is_wf hs ht a` is the set of all pairs of an element in `s` and an
element in `t` that multiply to `a`, but its construction requires proofs that `s` and `t` are
well-ordered. -/
@[to_additive
      "`finset.add_antidiagonal_of_is_wf hs ht a` is the set of all pairs of an element in\n`s` and an element in `t` that add to `a`, but its construction requires proofs that `s` and `t` are\nwell-ordered."]
noncomputable def mulAntidiagonal : Finset (α × α) :=
  (Set.MulAntidiagonal.finite_of_isPwo hs ht a).toFinset
#align finset.mul_antidiagonal Finset.mulAntidiagonal
#align finset.add_antidiagonal Finset.addAntidiagonal
-/

variable {hs ht a} {u : Set α} {hu : u.IsPwo} {x : α × α}

/- warning: finset.mem_mul_antidiagonal -> Finset.mem_mulAntidiagonal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedCancelCommMonoid.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {hs : Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) s} {ht : Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) t} {a : α} {x : Prod.{u1, u1} α α}, Iff (Membership.Mem.{u1, u1} (Prod.{u1, u1} α α) (Finset.{u1} (Prod.{u1, u1} α α)) (Finset.hasMem.{u1} (Prod.{u1, u1} α α)) x (Finset.mulAntidiagonal.{u1} α _inst_1 s t hs ht a)) (And (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) (Prod.fst.{u1, u1} α α x) s) (And (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) (Prod.snd.{u1, u1} α α x) t) (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α _inst_1))))))) (Prod.fst.{u1, u1} α α x) (Prod.snd.{u1, u1} α α x)) a)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedCancelCommMonoid.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {hs : Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) s} {ht : Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) t} {a : α} {x : Prod.{u1, u1} α α}, Iff (Membership.mem.{u1, u1} (Prod.{u1, u1} α α) (Finset.{u1} (Prod.{u1, u1} α α)) (Finset.instMembershipFinset.{u1} (Prod.{u1, u1} α α)) x (Finset.mulAntidiagonal.{u1} α _inst_1 s t hs ht a)) (And (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) (Prod.fst.{u1, u1} α α x) s) (And (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) (Prod.snd.{u1, u1} α α x) t) (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α _inst_1))))))) (Prod.fst.{u1, u1} α α x) (Prod.snd.{u1, u1} α α x)) a)))
Case conversion may be inaccurate. Consider using '#align finset.mem_mul_antidiagonal Finset.mem_mulAntidiagonalₓ'. -/
@[simp, to_additive]
theorem mem_mulAntidiagonal : x ∈ mulAntidiagonal hs ht a ↔ x.1 ∈ s ∧ x.2 ∈ t ∧ x.1 * x.2 = a := by
  simp [mul_antidiagonal, and_rotate]
#align finset.mem_mul_antidiagonal Finset.mem_mulAntidiagonal
#align finset.mem_add_antidiagonal Finset.mem_addAntidiagonal

#print Finset.mulAntidiagonal_mono_left /-
@[to_additive]
theorem mulAntidiagonal_mono_left (h : u ⊆ s) : mulAntidiagonal hu ht a ⊆ mulAntidiagonal hs ht a :=
  Set.Finite.toFinset_mono <| Set.mulAntidiagonal_mono_left h
#align finset.mul_antidiagonal_mono_left Finset.mulAntidiagonal_mono_left
#align finset.add_antidiagonal_mono_left Finset.addAntidiagonal_mono_left
-/

#print Finset.mulAntidiagonal_mono_right /-
@[to_additive]
theorem mulAntidiagonal_mono_right (h : u ⊆ t) :
    mulAntidiagonal hs hu a ⊆ mulAntidiagonal hs ht a :=
  Set.Finite.toFinset_mono <| Set.mulAntidiagonal_mono_right h
#align finset.mul_antidiagonal_mono_right Finset.mulAntidiagonal_mono_right
#align finset.add_antidiagonal_mono_right Finset.addAntidiagonal_mono_right
-/

#print Finset.swap_mem_mulAntidiagonal /-
@[simp, to_additive]
theorem swap_mem_mulAntidiagonal :
    x.symm ∈ Finset.mulAntidiagonal hs ht a ↔ x ∈ Finset.mulAntidiagonal ht hs a := by
  simp [mul_comm, and_left_comm]
#align finset.swap_mem_mul_antidiagonal Finset.swap_mem_mulAntidiagonal
#align finset.swap_mem_add_antidiagonal Finset.swap_mem_addAntidiagonal
-/

/- warning: finset.support_mul_antidiagonal_subset_mul -> Finset.support_mulAntidiagonal_subset_mul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedCancelCommMonoid.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {hs : Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) s} {ht : Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) t}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (setOf.{u1} α (fun (a : α) => Finset.Nonempty.{u1} (Prod.{u1, u1} α α) (Finset.mulAntidiagonal.{u1} α _inst_1 s t hs ht a))) (HMul.hMul.{u1, u1, u1} (Set.{u1} α) (Set.{u1} α) (Set.{u1} α) (instHMul.{u1} (Set.{u1} α) (Set.mul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α _inst_1)))))))) s t)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedCancelCommMonoid.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {hs : Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) s} {ht : Set.IsPwo.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α _inst_1)) t}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (setOf.{u1} α (fun (a : α) => Finset.Nonempty.{u1} (Prod.{u1, u1} α α) (Finset.mulAntidiagonal.{u1} α _inst_1 s t hs ht a))) (HMul.hMul.{u1, u1, u1} (Set.{u1} α) (Set.{u1} α) (Set.{u1} α) (instHMul.{u1} (Set.{u1} α) (Set.mul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α _inst_1)))))))) s t)
Case conversion may be inaccurate. Consider using '#align finset.support_mul_antidiagonal_subset_mul Finset.support_mulAntidiagonal_subset_mulₓ'. -/
@[to_additive]
theorem support_mulAntidiagonal_subset_mul : { a | (mulAntidiagonal hs ht a).Nonempty } ⊆ s * t :=
  fun a ⟨b, hb⟩ => by
  rw [mem_mul_antidiagonal] at hb
  exact ⟨b.1, b.2, hb⟩
#align finset.support_mul_antidiagonal_subset_mul Finset.support_mulAntidiagonal_subset_mul
#align finset.support_add_antidiagonal_subset_add Finset.support_addAntidiagonal_subset_add

#print Finset.isPwo_support_mulAntidiagonal /-
@[to_additive]
theorem isPwo_support_mulAntidiagonal : { a | (mulAntidiagonal hs ht a).Nonempty }.IsPwo :=
  (hs.mul ht).mono support_mulAntidiagonal_subset_mul
#align finset.is_pwo_support_mul_antidiagonal Finset.isPwo_support_mulAntidiagonal
#align finset.is_pwo_support_add_antidiagonal Finset.isPwo_support_addAntidiagonal
-/

/- warning: finset.mul_antidiagonal_min_mul_min -> Finset.mulAntidiagonal_min_mul_min is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : LinearOrderedCancelCommMonoid.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} (hs : Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2)))) s) (ht : Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2)))) t) (hns : Set.Nonempty.{u1} α s) (hnt : Set.Nonempty.{u1} α t), Eq.{succ u1} (Finset.{u1} (Prod.{u1, u1} α α)) (Finset.mulAntidiagonal.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2) s t (Set.IsWf.isPwo.{u1} α (LinearOrderedCommMonoid.toLinearOrder.{u1} α (LinearOrderedCancelCommMonoid.toLinearOrderedCommMonoid.{u1} α _inst_2)) s hs) (Set.IsWf.isPwo.{u1} α (LinearOrderedCommMonoid.toLinearOrder.{u1} α (LinearOrderedCancelCommMonoid.toLinearOrderedCommMonoid.{u1} α _inst_2)) t ht) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2)))))))) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2))) s hs hns) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2))) t ht hnt))) (Singleton.singleton.{u1, u1} (Prod.{u1, u1} α α) (Finset.{u1} (Prod.{u1, u1} α α)) (Finset.hasSingleton.{u1} (Prod.{u1, u1} α α)) (Prod.mk.{u1, u1} α α (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2))) s hs hns) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2))) t ht hnt)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : LinearOrderedCancelCommMonoid.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} (hs : Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2)))) s) (ht : Set.IsWf.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2)))) t) (hns : Set.Nonempty.{u1} α s) (hnt : Set.Nonempty.{u1} α t), Eq.{succ u1} (Finset.{u1} (Prod.{u1, u1} α α)) (Finset.mulAntidiagonal.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2) s t (Set.IsWf.isPwo.{u1} α (LinearOrderedCommMonoid.toLinearOrder.{u1} α (LinearOrderedCancelCommMonoid.toLinearOrderedCommMonoid.{u1} α _inst_2)) s hs) (Set.IsWf.isPwo.{u1} α (LinearOrderedCommMonoid.toLinearOrder.{u1} α (LinearOrderedCancelCommMonoid.toLinearOrderedCommMonoid.{u1} α _inst_2)) t ht) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (RightCancelMonoid.toMonoid.{u1} α (CancelMonoid.toRightCancelMonoid.{u1} α (CancelCommMonoid.toCancelMonoid.{u1} α (OrderedCancelCommMonoid.toCancelCommMonoid.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2)))))))) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2))) s hs hns) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2))) t ht hnt))) (Singleton.singleton.{u1, u1} (Prod.{u1, u1} α α) (Finset.{u1} (Prod.{u1, u1} α α)) (Finset.instSingletonFinset.{u1} (Prod.{u1, u1} α α)) (Prod.mk.{u1, u1} α α (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2))) s hs hns) (Set.IsWf.min.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedCancelCommMonoid.toPartialOrder.{u1} α (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{u1} α _inst_2))) t ht hnt)))
Case conversion may be inaccurate. Consider using '#align finset.mul_antidiagonal_min_mul_min Finset.mulAntidiagonal_min_mul_minₓ'. -/
@[to_additive]
theorem mulAntidiagonal_min_mul_min {α} [LinearOrderedCancelCommMonoid α] {s t : Set α}
    (hs : s.IsWf) (ht : t.IsWf) (hns : s.Nonempty) (hnt : t.Nonempty) :
    mulAntidiagonal hs.IsPwo ht.IsPwo (hs.min hns * ht.min hnt) = {(hs hns, ht hnt)} :=
  by
  ext ⟨a, b⟩
  simp only [mem_mul_antidiagonal, mem_singleton, Prod.ext_iff]
  constructor
  · rintro ⟨has, hat, hst⟩
    obtain rfl :=
      (hs.min_le hns has).eq_of_not_lt fun hlt =>
        (mul_lt_mul_of_lt_of_le hlt <| ht.min_le hnt hat).ne' hst
    exact ⟨rfl, mul_left_cancel hst⟩
  · rintro ⟨rfl, rfl⟩
    exact ⟨hs.min_mem _, ht.min_mem _, rfl⟩
#align finset.mul_antidiagonal_min_mul_min Finset.mulAntidiagonal_min_mul_min
#align finset.add_antidiagonal_min_add_min Finset.addAntidiagonal_min_add_min

end Finset