/-
Copyright (c) 2021 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.pnat.interval
! leanprover-community/mathlib commit 68d1483e8a718ec63219f0e227ca3f0140361086
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Nat.Interval
import Mathbin.Data.Pnat.Defs

/-!
# Finite intervals of positive naturals

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

This file proves that `ℕ+` is a `locally_finite_order` and calculates the cardinality of its
intervals as finsets and fintypes.
-/

open Finset PNat

instance : LocallyFiniteOrder ℕ+ :=
  Subtype.locallyFiniteOrder _

namespace PNat

variable (a b : ℕ+)

/- warning: pnat.Icc_eq_finset_subtype -> PNat.Icc_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} PNat) (Finset.Icc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b) (Finset.subtype.{0} Nat (fun (n : Nat) => LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n) (fun (a : Nat) => Nat.decidableLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) a) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b)))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} PNat) (Finset.Icc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b) (Finset.subtype.{0} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) (fun (a : Nat) => Nat.decLt (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) a) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (PNat.val a) (PNat.val b)))
Case conversion may be inaccurate. Consider using '#align pnat.Icc_eq_finset_subtype PNat.Icc_eq_finset_subtypeₓ'. -/
theorem Icc_eq_finset_subtype : Icc a b = (Icc (a : ℕ) b).Subtype fun n : ℕ => 0 < n :=
  rfl
#align pnat.Icc_eq_finset_subtype PNat.Icc_eq_finset_subtype

/- warning: pnat.Ico_eq_finset_subtype -> PNat.Ico_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} PNat) (Finset.Ico.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b) (Finset.subtype.{0} Nat (fun (n : Nat) => LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n) (fun (a : Nat) => Nat.decidableLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) a) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b)))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} PNat) (Finset.Ico.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b) (Finset.subtype.{0} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) (fun (a : Nat) => Nat.decLt (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) a) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (PNat.val a) (PNat.val b)))
Case conversion may be inaccurate. Consider using '#align pnat.Ico_eq_finset_subtype PNat.Ico_eq_finset_subtypeₓ'. -/
theorem Ico_eq_finset_subtype : Ico a b = (Ico (a : ℕ) b).Subtype fun n : ℕ => 0 < n :=
  rfl
#align pnat.Ico_eq_finset_subtype PNat.Ico_eq_finset_subtype

/- warning: pnat.Ioc_eq_finset_subtype -> PNat.Ioc_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} PNat) (Finset.Ioc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b) (Finset.subtype.{0} Nat (fun (n : Nat) => LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n) (fun (a : Nat) => Nat.decidableLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) a) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b)))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} PNat) (Finset.Ioc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b) (Finset.subtype.{0} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) (fun (a : Nat) => Nat.decLt (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) a) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (PNat.val a) (PNat.val b)))
Case conversion may be inaccurate. Consider using '#align pnat.Ioc_eq_finset_subtype PNat.Ioc_eq_finset_subtypeₓ'. -/
theorem Ioc_eq_finset_subtype : Ioc a b = (Ioc (a : ℕ) b).Subtype fun n : ℕ => 0 < n :=
  rfl
#align pnat.Ioc_eq_finset_subtype PNat.Ioc_eq_finset_subtype

/- warning: pnat.Ioo_eq_finset_subtype -> PNat.Ioo_eq_finset_subtype is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} PNat) (Finset.Ioo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b) (Finset.subtype.{0} Nat (fun (n : Nat) => LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n) (fun (a : Nat) => Nat.decidableLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) a) (Finset.Ioo.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b)))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} PNat) (Finset.Ioo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b) (Finset.subtype.{0} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) (fun (a : Nat) => Nat.decLt (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) a) (Finset.Ioo.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (PNat.val a) (PNat.val b)))
Case conversion may be inaccurate. Consider using '#align pnat.Ioo_eq_finset_subtype PNat.Ioo_eq_finset_subtypeₓ'. -/
theorem Ioo_eq_finset_subtype : Ioo a b = (Ioo (a : ℕ) b).Subtype fun n : ℕ => 0 < n :=
  rfl
#align pnat.Ioo_eq_finset_subtype PNat.Ioo_eq_finset_subtype

/- warning: pnat.map_subtype_embedding_Icc -> PNat.map_subtype_embedding_Icc is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} PNat Nat (Function.Embedding.subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n)) (Finset.Icc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) Nat (Function.Embedding.subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) (Finset.Icc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (PNat.val a) (PNat.val b))
Case conversion may be inaccurate. Consider using '#align pnat.map_subtype_embedding_Icc PNat.map_subtype_embedding_Iccₓ'. -/
theorem map_subtype_embedding_Icc : (Icc a b).map (Function.Embedding.subtype _) = Icc (a : ℕ) b :=
  map_subtype_embedding_Icc _ _ _ fun c _ x hx _ hc _ => hc.trans_le hx
#align pnat.map_subtype_embedding_Icc PNat.map_subtype_embedding_Icc

/- warning: pnat.map_subtype_embedding_Ico -> PNat.map_subtype_embedding_Ico is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} PNat Nat (Function.Embedding.subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n)) (Finset.Ico.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) Nat (Function.Embedding.subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) (Finset.Ico.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (PNat.val a) (PNat.val b))
Case conversion may be inaccurate. Consider using '#align pnat.map_subtype_embedding_Ico PNat.map_subtype_embedding_Icoₓ'. -/
theorem map_subtype_embedding_Ico : (Ico a b).map (Function.Embedding.subtype _) = Ico (a : ℕ) b :=
  map_subtype_embedding_Ico _ _ _ fun c _ x hx _ hc _ => hc.trans_le hx
#align pnat.map_subtype_embedding_Ico PNat.map_subtype_embedding_Ico

/- warning: pnat.map_subtype_embedding_Ioc -> PNat.map_subtype_embedding_Ioc is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} PNat Nat (Function.Embedding.subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n)) (Finset.Ioc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) Nat (Function.Embedding.subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) (Finset.Ioc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (PNat.val a) (PNat.val b))
Case conversion may be inaccurate. Consider using '#align pnat.map_subtype_embedding_Ioc PNat.map_subtype_embedding_Iocₓ'. -/
theorem map_subtype_embedding_Ioc : (Ioc a b).map (Function.Embedding.subtype _) = Ioc (a : ℕ) b :=
  map_subtype_embedding_Ioc _ _ _ fun c _ x hx _ hc _ => hc.trans_le hx
#align pnat.map_subtype_embedding_Ioc PNat.map_subtype_embedding_Ioc

/- warning: pnat.map_subtype_embedding_Ioo -> PNat.map_subtype_embedding_Ioo is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} PNat Nat (Function.Embedding.subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n)) (Finset.Ioo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (Finset.Ioo.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} (Finset.{0} Nat) (Finset.map.{0, 0} (Subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) Nat (Function.Embedding.subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) (Finset.Ioo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (Finset.Ioo.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (PNat.val a) (PNat.val b))
Case conversion may be inaccurate. Consider using '#align pnat.map_subtype_embedding_Ioo PNat.map_subtype_embedding_Iooₓ'. -/
theorem map_subtype_embedding_Ioo : (Ioo a b).map (Function.Embedding.subtype _) = Ioo (a : ℕ) b :=
  map_subtype_embedding_Ioo _ _ _ fun c _ x hx _ hc _ => hc.trans_le hx
#align pnat.map_subtype_embedding_Ioo PNat.map_subtype_embedding_Ioo

/- warning: pnat.card_Icc -> PNat.card_Icc is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Finset.card.{0} PNat (Finset.Icc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Finset.card.{0} PNat (Finset.Icc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (PNat.val b) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (PNat.val a))
Case conversion may be inaccurate. Consider using '#align pnat.card_Icc PNat.card_Iccₓ'. -/
@[simp]
theorem card_Icc : (Icc a b).card = b + 1 - a := by
  rw [← Nat.card_Icc, ← map_subtype_embedding_Icc, card_map]
#align pnat.card_Icc PNat.card_Icc

/- warning: pnat.card_Ico -> PNat.card_Ico is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Finset.card.{0} PNat (Finset.Ico.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Finset.card.{0} PNat (Finset.Ico.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (PNat.val b) (PNat.val a))
Case conversion may be inaccurate. Consider using '#align pnat.card_Ico PNat.card_Icoₓ'. -/
@[simp]
theorem card_Ico : (Ico a b).card = b - a := by
  rw [← Nat.card_Ico, ← map_subtype_embedding_Ico, card_map]
#align pnat.card_Ico PNat.card_Ico

/- warning: pnat.card_Ioc -> PNat.card_Ioc is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Finset.card.{0} PNat (Finset.Ioc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Finset.card.{0} PNat (Finset.Ioc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (PNat.val b) (PNat.val a))
Case conversion may be inaccurate. Consider using '#align pnat.card_Ioc PNat.card_Iocₓ'. -/
@[simp]
theorem card_Ioc : (Ioc a b).card = b - a := by
  rw [← Nat.card_Ioc, ← map_subtype_embedding_Ioc, card_map]
#align pnat.card_Ioc PNat.card_Ioc

/- warning: pnat.card_Ioo -> PNat.card_Ioo is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Finset.card.{0} PNat (Finset.Ioo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a)) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Finset.card.{0} PNat (Finset.Ioo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (PNat.val b) (PNat.val a)) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align pnat.card_Ioo PNat.card_Iooₓ'. -/
@[simp]
theorem card_Ioo : (Ioo a b).card = b - a - 1 := by
  rw [← Nat.card_Ioo, ← map_subtype_embedding_Ioo, card_map]
#align pnat.card_Ioo PNat.card_Ioo

/- warning: pnat.card_fintype_Icc -> PNat.card_fintype_Icc is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} PNat) Type (Set.hasCoeToSort.{0} PNat) (Set.Icc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) a b)) (Set.fintypeIcc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} PNat (Set.Icc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) a b)) (Set.fintypeIcc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (PNat.val b) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (PNat.val a))
Case conversion may be inaccurate. Consider using '#align pnat.card_fintype_Icc PNat.card_fintype_Iccₓ'. -/
@[simp]
theorem card_fintype_Icc : Fintype.card (Set.Icc a b) = b + 1 - a := by
  rw [← card_Icc, Fintype.card_ofFinset]
#align pnat.card_fintype_Icc PNat.card_fintype_Icc

/- warning: pnat.card_fintype_Ico -> PNat.card_fintype_Ico is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} PNat) Type (Set.hasCoeToSort.{0} PNat) (Set.Ico.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) a b)) (Set.fintypeIco.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} PNat (Set.Ico.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) a b)) (Set.fintypeIco.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (PNat.val b) (PNat.val a))
Case conversion may be inaccurate. Consider using '#align pnat.card_fintype_Ico PNat.card_fintype_Icoₓ'. -/
@[simp]
theorem card_fintype_Ico : Fintype.card (Set.Ico a b) = b - a := by
  rw [← card_Ico, Fintype.card_ofFinset]
#align pnat.card_fintype_Ico PNat.card_fintype_Ico

/- warning: pnat.card_fintype_Ioc -> PNat.card_fintype_Ioc is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} PNat) Type (Set.hasCoeToSort.{0} PNat) (Set.Ioc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) a b)) (Set.fintypeIoc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} PNat (Set.Ioc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) a b)) (Set.fintypeIoc.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (PNat.val b) (PNat.val a))
Case conversion may be inaccurate. Consider using '#align pnat.card_fintype_Ioc PNat.card_fintype_Iocₓ'. -/
@[simp]
theorem card_fintype_Ioc : Fintype.card (Set.Ioc a b) = b - a := by
  rw [← card_Ioc, Fintype.card_ofFinset]
#align pnat.card_fintype_Ioc PNat.card_fintype_Ioc

/- warning: pnat.card_fintype_Ioo -> PNat.card_fintype_Ioo is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Fintype.card.{0} (coeSort.{1, 2} (Set.{0} PNat) Type (Set.hasCoeToSort.{0} PNat) (Set.Ioo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) a b)) (Set.fintypeIoo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (LinearOrder.toLattice.{0} PNat PNat.linearOrder)))) PNat.locallyFiniteOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) b) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PNat Nat (HasLiftT.mk.{1, 1} PNat Nat (CoeTCₓ.coe.{1, 1} PNat Nat (coeBase.{1, 1} PNat Nat coePNatNat))) a)) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall (a : PNat) (b : PNat), Eq.{1} Nat (Fintype.card.{0} (Set.Elem.{0} PNat (Set.Ioo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) a b)) (Set.fintypeIoo.{0} PNat (PartialOrder.toPreorder.{0} PNat (SemilatticeInf.toPartialOrder.{0} PNat (Lattice.toSemilatticeInf.{0} PNat (DistribLattice.toLattice.{0} PNat (instDistribLattice.{0} PNat instPNatLinearOrder))))) instLocallyFiniteOrderPNatToPreorderToPartialOrderToSemilatticeInfToLatticeInstDistribLatticeInstPNatLinearOrder a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (PNat.val b) (PNat.val a)) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align pnat.card_fintype_Ioo PNat.card_fintype_Iooₓ'. -/
@[simp]
theorem card_fintype_Ioo : Fintype.card (Set.Ioo a b) = b - a - 1 := by
  rw [← card_Ioo, Fintype.card_ofFinset]
#align pnat.card_fintype_Ioo PNat.card_fintype_Ioo

end PNat


open tactic
open smt_tactic

meta def blast : tactic unit := using_smt $ intros >> try simp >> try eblast

notation `♮` := by abstract { blast }

universe variables u v

structure Category :=
  (Obj : Type u)
  (Hom : Obj → Obj → Type v)
  (identity : Π X : Obj, Hom X X)
  (compose  : Π { X Y Z : Obj }, Hom X Y → Hom Y Z → Hom X Z)

  (left_identity  : ∀ { X Y : Obj } (f : Hom X Y), compose (identity X) f = f)
  (right_identity : ∀ { X Y : Obj } (f : Hom X Y), compose f (identity Y) = f)
  (associativity  : ∀ { W X Y Z : Obj } (f : Hom W X) (g : Hom X Y) (h : Hom Y Z),
    compose (compose f g) h = compose f (compose g h))

attribute [ematch,simp] Category.left_identity
attribute [ematch,simp] Category.right_identity
attribute [ematch] Category.associativity

universe variables u1 v1 u2 v2

structure Functor (C : Category.{ u1 v1 }) (D : Category.{ u2 v2 }) :=
  (onObjects   : C^.Obj → D^.Obj)
  (onMorphisms : Π { X Y : C^.Obj },
                C^.Hom X Y → D^.Hom (onObjects X) (onObjects Y))
  (identities : ∀ (X : C^.Obj),
    onMorphisms (C^.identity X) = D^.identity (onObjects X))
  (functoriality : ∀ { X Y Z : C^.Obj } (f : C^.Hom X Y) (g : C^.Hom Y Z),
    onMorphisms (C^.compose f g) = D^.compose (onMorphisms f) (onMorphisms g))

attribute [simp,ematch] Functor.identities
attribute [simp,ematch] Functor.functoriality

instance Functor_to_onObjects { C D : Category }: has_coe_to_fun (Functor C D) :=
{ F   := λ f, C^.Obj → D^.Obj,
  coe := Functor.onObjects }

structure NaturalTransformation { C D : Category } ( F G : Functor C D ) :=
  (components: Π X : C^.Obj, D^.Hom (F X) (G X))
  (naturality: ∀ { X Y : C^.Obj } (f : C^.Hom X Y),
     D^.compose (F^.onMorphisms f) (components Y) = D^.compose (components X) (G^.onMorphisms f))

attribute [ematch] NaturalTransformation.naturality

instance NaturalTransformation_to_components { C D : Category } { F G : Functor C D } : has_coe_to_fun (NaturalTransformation F G) :=
{ F   := λ f, Π X : C^.Obj, D^.Hom (F X) (G X),
  coe := NaturalTransformation.components }

lemma NaturalTransformations_componentwise_equal
  { C D : Category }
  { F G : Functor C D }
  ( α β : NaturalTransformation F G )
  ( w : ∀ X : C^.Obj, α X = β X ) : α = β :=
  begin
    induction α with α_components α_naturality,
    induction β with β_components β_naturality,
    have hc : α_components = β_components, from funext w,
    by subst hc
  end

@[reducible] definition IdentityNaturalTransformation { C D : Category } (F : Functor C D) : NaturalTransformation F F :=
  {
    components := λ X, D^.identity (F X),
    naturality := ♮
  }

@[reducible] definition vertical_composition_of_NaturalTransformations
  { C D : Category }
  { F G H : Functor C D }
  ( α : NaturalTransformation F G )
  ( β : NaturalTransformation G H ) : NaturalTransformation F H :=
  {
    components := λ X, D^.compose (α X) (β X),
    naturality := ♮
  }

definition FunctorCategory ( C D : Category ) : Category :=
{
  Obj := Functor C D,
  Hom := λ F G, NaturalTransformation F G,

  identity := λ F, IdentityNaturalTransformation F,
  compose  := @vertical_composition_of_NaturalTransformations C D,

  left_identity  := begin blast, apply NaturalTransformations_componentwise_equal, blast end,
  right_identity := begin blast, apply NaturalTransformations_componentwise_equal, blast end,
  associativity  := begin blast, apply NaturalTransformations_componentwise_equal, blast end
}

-- TODO This is pure boilerplate! How can we automatically form this lemma? Ideally we'd just annotate a field of FunctorCategory!
@[simp] lemma FunctorCategory_left_identity
  ( C D : Category )
  ( F G : Functor C D )
  ( α : NaturalTransformation F G ) : vertical_composition_of_NaturalTransformations (IdentityNaturalTransformation F) α = α :=
    (FunctorCategory C D)^.left_identity α


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

! This file was ported from Lean 3 source module category_theory.subobject.well_powered
! leanprover-community/mathlib commit 29b834dfbba4ed1b7950628bbf5e69ab98c15b4c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Subobject.Basic
import Mathbin.CategoryTheory.EssentiallySmall

/-!
# Well-powered categories

A category `(C : Type u) [category.{v} C]` is `[well_powered C]` if
for every `X : C`, we have `small.{v} (subobject X)`.

(Note that in this situtation `subobject X : Type (max u v)`,
so this is a nontrivial condition for large categories,
but automatic for small categories.)

This is equivalent to the category `mono_over X` being `essentially_small.{v}` for all `X : C`.

When a category is well-powered, you can obtain nonconstructive witnesses as
`shrink (subobject X) : Type v`
and
`equiv_shrink (subobject X) : subobject X ≃ shrink (subobject X)`.
-/

universe v u₁ u₂

namespace CategoryTheory

variable (C : Type u₁) [Category.{v} C]

/--
A category (with morphisms in `Type v`) is well-powered if `subobject X` is `v`-small for every `X`.

We show in `well_powered_of_mono_over_essentially_small` and `mono_over_essentially_small`
that this is the case if and only if `mono_over X` is `v`-essentially small for every `X`.
-/
class WellPowered : Prop where
  subobject_small : ∀ X : C, Small.{v} (Subobject X) := by infer_instance
#align category_theory.well_powered CategoryTheory.WellPowered

instance small_subobject [WellPowered C] (X : C) : Small.{v} (Subobject X) :=
  WellPowered.subobject_small X
#align category_theory.small_subobject CategoryTheory.small_subobject

instance (priority := 100) wellPowered_of_smallCategory (C : Type u₁) [SmallCategory C] :
    WellPowered C where
#align category_theory.well_powered_of_small_category CategoryTheory.wellPowered_of_smallCategory

variable {C}

theorem essentiallySmall_monoOver_iff_small_subobject (X : C) :
    EssentiallySmall.{v} (MonoOver X) ↔ Small.{v} (Subobject X) :=
  essentiallySmall_iff_of_thin
#align category_theory.essentially_small_mono_over_iff_small_subobject CategoryTheory.essentiallySmall_monoOver_iff_small_subobject

theorem wellPowered_of_essentiallySmall_monoOver (h : ∀ X : C, EssentiallySmall.{v} (MonoOver X)) :
    WellPowered C :=
  { subobject_small := fun X => (essentiallySmall_monoOver_iff_small_subobject X).mp (h X) }
#align category_theory.well_powered_of_essentially_small_mono_over CategoryTheory.wellPowered_of_essentiallySmall_monoOver

section

variable [WellPowered C]

instance essentiallySmall_monoOver (X : C) : EssentiallySmall.{v} (MonoOver X) :=
  (essentiallySmall_monoOver_iff_small_subobject X).mpr (WellPowered.subobject_small X)
#align category_theory.essentially_small_mono_over CategoryTheory.essentiallySmall_monoOver

end

section Equivalence

variable {D : Type u₂} [Category.{v} D]

theorem wellPowered_of_equiv (e : C ≌ D) [WellPowered C] : WellPowered D :=
  wellPowered_of_essentiallySmall_monoOver fun X =>
    (essentiallySmall_congr (MonoOver.congr X e.symm)).2 <| by infer_instance
#align category_theory.well_powered_of_equiv CategoryTheory.wellPowered_of_equiv

/-- Being well-powered is preserved by equivalences, as long as the two categories involved have
    their morphisms in the same universe. -/
theorem wellPowered_congr (e : C ≌ D) : WellPowered C ↔ WellPowered D :=
  ⟨fun i => well_powered_of_equiv e, fun i => well_powered_of_equiv e.symm⟩
#align category_theory.well_powered_congr CategoryTheory.wellPowered_congr

end Equivalence

end CategoryTheory


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

! This file was ported from Lean 3 source module group_theory.perm.list
! 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.List.Rotate
import Mathlib.GroupTheory.Perm.Support

/-!
# Permutations from a list

A list `l : List α` can be interpreted as a `Equiv.Perm α` where each element in the list
is permuted to the next one, defined as `formPerm`. When we have that `Nodup l`,
we prove that `Equiv.Perm.support (formPerm l) = l.toFinset`, and that
`formPerm l` is rotationally invariant, in `formPerm_rotate`.

When there are duplicate elements in `l`, how and in what arrangement with respect to the other
elements they appear in the list determines the formed permutation.
This is because `List.formPerm` is implemented as a product of `Equiv.swap`s.
That means that presence of a sublist of two adjacent duplicates like `[..., x, x, ...]`
will produce the same permutation as if the adjacent duplicates were not present.

The `List.formPerm` definition is meant to primarily be used with `Nodup l`, so that
the resulting permutation is cyclic (if `l` has at least two elements).
The presence of duplicates in a particular placement can lead `List.formPerm` to produce a
nontrivial permutation that is noncyclic.
-/

namespace List

variable {α β : Type _}

section FormPerm

variable [DecidableEq α] (l : List α)

open Equiv Equiv.Perm

/-- A list `l : List α` can be interpreted as a `Equiv.Perm α` where each element in the list
is permuted to the next one, defined as `formPerm`. When we have that `Nodup l`,
we prove that `Equiv.Perm.support (formPerm l) = l.toFinset`, and that
`formPerm l` is rotationally invariant, in `formPerm_rotate`.
-/
def formPerm : Equiv.Perm α :=
  (zipWith Equiv.swap l l.tail).prod
#align list.form_perm List.formPerm

@[simp]
theorem formPerm_nil : formPerm ([] : List α) = 1 :=
  rfl
#align list.form_perm_nil List.formPerm_nil

@[simp]
theorem formPerm_singleton (x : α) : formPerm [x] = 1 :=
  rfl
#align list.form_perm_singleton List.formPerm_singleton

@[simp]
theorem formPerm_cons_cons (x y : α) (l : List α) :
    formPerm (x :: y :: l) = swap x y * formPerm (y :: l) :=
  prod_cons
#align list.form_perm_cons_cons List.formPerm_cons_cons

theorem formPerm_pair (x y : α) : formPerm [x, y] = swap x y :=
  rfl
#align list.form_perm_pair List.formPerm_pair

variable {l} {x : α}

theorem formPerm_apply_of_not_mem (x : α) (l : List α) (h : x ∉ l) : formPerm l x = x := by
  cases' l with y l
  · simp
  induction' l with z l IH generalizing x y
  · simp
  · specialize IH x z (mt (mem_cons_of_mem y) h)
    simp only [not_or, mem_cons] at h
    simp [IH, swap_apply_of_ne_of_ne, h]
#align list.form_perm_apply_of_not_mem List.formPerm_apply_of_not_mem

theorem mem_of_formPerm_apply_ne (x : α) (l : List α) : l.formPerm x ≠ x → x ∈ l :=
  not_imp_comm.2 <| List.formPerm_apply_of_not_mem _ _
#align list.mem_of_form_perm_apply_ne List.mem_of_formPerm_apply_ne

theorem formPerm_apply_mem_of_mem (x : α) (l : List α) (h : x ∈ l) : formPerm l x ∈ l := by
  cases' l with y l
  · simp at h
  induction' l with z l IH generalizing x y
  · simpa using h
  · by_cases hx : x ∈ z :: l
    · rw [formPerm_cons_cons, mul_apply, swap_apply_def]
      split_ifs
      . simp [IH _ _ hx]
      . simp
      . simpa [*] using IH _ _ hx
    · replace h : x = y := Or.resolve_right (mem_cons.1 h) hx
      simp [formPerm_apply_of_not_mem _ _ hx, ← h]
#align list.form_perm_apply_mem_of_mem List.formPerm_apply_mem_of_mem

theorem mem_of_formPerm_apply_mem (x : α) (l : List α) (h : l.formPerm x ∈ l) : x ∈ l := by
  cases' l with y l
  · simp at h
  induction' l with z l IH generalizing x y
  · simpa using h
  · by_cases hx : (z :: l).formPerm x ∈ z :: l
    · rw [List.formPerm_cons_cons, mul_apply, swap_apply_def] at h
      split_ifs at h <;> aesop
    · replace hx :=
        (Function.Injective.eq_iff (Equiv.injective _)).mp (List.formPerm_apply_of_not_mem _ _ hx)
      simp only [List.formPerm_cons_cons, hx, Equiv.Perm.coe_mul, Function.comp_apply,
        List.mem_cons, swap_apply_def, ite_eq_left_iff] at h
      simp only [List.mem_cons]
      rcases h with h | h | h <;> split_ifs at h with h1 <;> try { aesop }
      . simp [h1, imp_false] at h
        simp [h]
#align list.mem_of_form_perm_apply_mem List.mem_of_formPerm_apply_mem

theorem formPerm_mem_iff_mem : l.formPerm x ∈ l ↔ x ∈ l :=
  ⟨l.mem_of_formPerm_apply_mem x, l.formPerm_apply_mem_of_mem x⟩
#align list.form_perm_mem_iff_mem List.formPerm_mem_iff_mem

@[simp]
theorem formPerm_cons_concat_apply_last (x y : α) (xs : List α) :
    formPerm (x :: (xs ++ [y])) y = x := by
  induction' xs with z xs IH generalizing x y
  · simp
  · simp [IH]
#align list.form_perm_cons_concat_apply_last List.formPerm_cons_concat_apply_last

@[simp]
theorem formPerm_apply_getLast (x : α) (xs : List α) :
    formPerm (x :: xs) ((x :: xs).getLast (cons_ne_nil x xs)) = x := by
  induction' xs using List.reverseRecOn with xs y _ generalizing x <;> simp
#align list.form_perm_apply_last List.formPerm_apply_getLast

set_option linter.deprecated false in
@[simp]
theorem formPerm_apply_nthLe_length (x : α) (xs : List α) :
    formPerm (x :: xs) ((x :: xs).nthLe xs.length (by simp)) = x := by
  rw [nthLe_cons_length, formPerm_apply_getLast]; rfl
#align list.form_perm_apply_nth_le_length List.formPerm_apply_nthLe_length

theorem formPerm_apply_head (x y : α) (xs : List α) (h : Nodup (x :: y :: xs)) :
    formPerm (x :: y :: xs) x = y := by simp [formPerm_apply_of_not_mem _ _ h.not_mem]
#align list.form_perm_apply_head List.formPerm_apply_head

set_option linter.deprecated false in
theorem formPerm_apply_nthLe_zero (l : List α) (h : Nodup l) (hl : 1 < l.length) :
    formPerm l (l.nthLe 0 (zero_lt_one.trans hl)) = l.nthLe 1 hl := by
  rcases l with (_ | ⟨x, _ | ⟨y, tl⟩⟩)
  · simp at hl
  · simp
  · simpa using formPerm_apply_head _ _ _ h
#align list.form_perm_apply_nth_le_zero List.formPerm_apply_nthLe_zero

variable (l)

theorem formPerm_eq_head_iff_eq_getLast (x y : α) :
    formPerm (y :: l) x = y ↔ x = getLast (y :: l) (cons_ne_nil _ _) :=
  Iff.trans (by rw [formPerm_apply_getLast]) (formPerm (y :: l)).injective.eq_iff
#align list.form_perm_eq_head_iff_eq_last List.formPerm_eq_head_iff_eq_getLast

theorem zipWith_swap_prod_support [Fintype α] (l l' : List α) :
    (zipWith swap l l').prod.support ≤ l.toFinset ⊔ l'.toFinset := by
  intro x hx
  have hx' : x ∈ { x | (zipWith swap l l').prod x ≠ x } := by simpa using hx
  simpa using zipWith_swap_prod_support' _ _ hx'
#align list.zip_with_swap_prod_support List.zipWith_swap_prod_support

theorem support_formPerm_le' : { x | formPerm l x ≠ x } ≤ l.toFinset := by
  refine' (zipWith_swap_prod_support' l l.tail).trans _
  simpa [Finset.subset_iff] using tail_subset l
#align list.support_form_perm_le' List.support_formPerm_le'

theorem support_formPerm_le [Fintype α] : support (formPerm l) ≤ l.toFinset := by
  intro x hx
  have hx' : x ∈ { x | formPerm l x ≠ x } := by simpa using hx
  simpa using support_formPerm_le' _ hx'
#align list.support_form_perm_le List.support_formPerm_le

set_option linter.deprecated false in
theorem formPerm_apply_lt (xs : List α) (h : Nodup xs) (n : ℕ) (hn : n + 1 < xs.length) :
    formPerm xs (xs.nthLe n ((Nat.lt_succ_self n).trans hn)) = xs.nthLe (n + 1) hn := by
  induction' n with n IH generalizing xs
  · simpa using formPerm_apply_nthLe_zero _ h _
  · rcases xs with (_ | ⟨x, _ | ⟨y, l⟩⟩)
    · simp at hn
    · simp
    · specialize IH (y :: l) h.of_cons _
      · simpa [Nat.succ_lt_succ_iff] using hn
      simp only [swap_apply_eq_iff, coe_mul, formPerm_cons_cons, Function.comp]
      simp only [nthLe, get_cons_succ] at *
      rw [← IH, swap_apply_of_ne_of_ne] <;>
      . intro hx
        rw [← hx, IH] at h
        simp [get_mem] at h
#align list.form_perm_apply_lt List.formPerm_apply_lt

set_option linter.deprecated false in
theorem formPerm_apply_nthLe (xs : List α) (h : Nodup xs) (n : ℕ) (hn : n < xs.length) :
    formPerm xs (xs.nthLe n hn) =
      xs.nthLe ((n + 1) % xs.length) (Nat.mod_lt _ (n.zero_le.trans_lt hn)) := by
  cases' xs with x xs
  · simp at hn
  · have : n ≤ xs.length := by
      refine' Nat.le_of_lt_succ _
      simpa using hn
    rcases this.eq_or_lt with (rfl | hn')
    · simp; simp [nthLe]
    · rw [formPerm_apply_lt _ h _ (Nat.succ_lt_succ hn')]
      congr
      rw [Nat.mod_eq_of_lt]; simpa [Nat.succ_eq_add_one]
#align list.form_perm_apply_nth_le List.formPerm_apply_nthLe

set_option linter.deprecated false in
theorem support_formPerm_of_nodup' (l : List α) (h : Nodup l) (h' : ∀ x : α, l ≠ [x]) :
    { x | formPerm l x ≠ x } = l.toFinset := by
  apply _root_.le_antisymm
  · exact support_formPerm_le' l
  · intro x hx
    simp only [Finset.mem_coe, mem_toFinset] at hx
    obtain ⟨n, hn, rfl⟩ := nthLe_of_mem hx
    rw [Set.mem_setOf_eq, formPerm_apply_nthLe _ h]
    intro H
    rw [nodup_iff_nthLe_inj] at h
    specialize h _ _ _ _ H
    cases' (Nat.succ_le_of_lt hn).eq_or_lt with hn' hn'
    · simp only [← hn', Nat.mod_self] at h
      refine' not_exists.mpr h' _
      rw [← length_eq_one]
      simpa [← h, eq_comm] using hn'
    · simp [Nat.mod_eq_of_lt hn'] at h
#align list.support_form_perm_of_nodup' List.support_formPerm_of_nodup'

theorem support_formPerm_of_nodup [Fintype α] (l : List α) (h : Nodup l) (h' : ∀ x : α, l ≠ [x]) :
    support (formPerm l) = l.toFinset := by
  rw [← Finset.coe_inj]
  convert support_formPerm_of_nodup' _ h h'
  simp [Set.ext_iff]
#align list.support_form_perm_of_nodup List.support_formPerm_of_nodup

set_option linter.deprecated false in
theorem formPerm_rotate_one (l : List α) (h : Nodup l) : formPerm (l.rotate 1) = formPerm l :=
  by
  have h' : Nodup (l.rotate 1) := by simpa using h
  ext x
  by_cases hx : x ∈ l.rotate 1
  · obtain ⟨k, hk, rfl⟩ := nthLe_of_mem hx
    rw [formPerm_apply_nthLe _ h', nthLe_rotate l, nthLe_rotate l, formPerm_apply_nthLe _ h]
    simp
  · rw [formPerm_apply_of_not_mem _ _ hx, formPerm_apply_of_not_mem]
    simpa using hx
#align list.form_perm_rotate_one List.formPerm_rotate_one

theorem formPerm_rotate (l : List α) (h : Nodup l) (n : ℕ) :
    formPerm (l.rotate n) = formPerm l := by
  induction' n with n hn
  · simp
  · rw [Nat.succ_eq_add_one, ← rotate_rotate, formPerm_rotate_one, hn]
    rwa [IsRotated.nodup_iff]
    exact IsRotated.forall l n
#align list.form_perm_rotate List.formPerm_rotate

theorem formPerm_eq_of_isRotated {l l' : List α} (hd : Nodup l) (h : l ~r l') :
    formPerm l = formPerm l' := by
  obtain ⟨n, rfl⟩ := h
  exact (formPerm_rotate l hd n).symm
#align list.form_perm_eq_of_is_rotated List.formPerm_eq_of_isRotated

set_option linter.deprecated false in
theorem formPerm_reverse (l : List α) (h : Nodup l) : formPerm l.reverse = (formPerm l)⁻¹ := by
  -- Let's show `formPerm l` is an inverse to `formPerm l.reverse`.
  rw [eq_comm, inv_eq_iff_mul_eq_one]
  ext x
  -- We only have to check for `x ∈ l` that `formPerm l (formPerm l.reverse x)`
  rw [mul_apply, one_apply]
  cases' Classical.em (x ∈ l) with hx hx
  . obtain ⟨k, hk, rfl⟩ := nthLe_of_mem ((mem_reverse _ _).mpr hx)
    have h1 : l.length - 1 - k < l.length := by
      rw [Nat.sub_sub, add_comm]
      exact Nat.sub_lt_self (Nat.succ_pos _) (Nat.succ_le_of_lt (by simpa using hk))
    have h2 : length l - 1 - (k + 1) % length (reverse l) < length l := by
      rw [Nat.sub_sub, length_reverse];
      exact Nat.sub_lt_self (by rw [add_comm]; exact Nat.succ_pos _)
        (by rw [add_comm]; exact Nat.succ_le_of_lt (Nat.mod_lt _ (length_pos_of_mem hx)))
    rw [formPerm_apply_nthLe l.reverse (nodup_reverse.mpr h), nthLe_reverse' _ _ _ h1,
      nthLe_reverse' _ _ _ h2, formPerm_apply_nthLe _ h]
    congr
    rw [length_reverse] at *
    cases' lt_or_eq_of_le (Nat.succ_le_of_lt hk) with h h
    . rw [Nat.mod_eq_of_lt h, ← Nat.sub_add_comm, Nat.succ_sub_succ_eq_sub,
        Nat.mod_eq_of_lt h1]
      exact (Nat.le_sub_iff_add_le (length_pos_of_mem hx)).2 (Nat.succ_le_of_lt h)
    . rw [← h]; simp
  · rw [formPerm_apply_of_not_mem x l.reverse, formPerm_apply_of_not_mem _ _ hx]
    simpa using hx
#align list.form_perm_reverse List.formPerm_reverse

theorem formPerm_pow_apply_nthLe (l : List α) (h : Nodup l) (n k : ℕ) (hk : k < l.length) :
    (formPerm l ^ n) (l.nthLe k hk) =
      l.nthLe ((k + n) % l.length) (Nat.mod_lt _ (k.zero_le.trans_lt hk)) :=
  by
  induction' n with n hn
  · simp [Nat.mod_eq_of_lt hk]
  · simp [pow_succ, mul_apply, hn, formPerm_apply_nthLe _ h, Nat.succ_eq_add_one, ← Nat.add_assoc]
#align list.form_perm_pow_apply_nth_le List.formPerm_pow_apply_nthLe

theorem formPerm_pow_apply_head (x : α) (l : List α) (h : Nodup (x :: l)) (n : ℕ) :
    (formPerm (x :: l) ^ n) x =
      (x :: l).nthLe (n % (x :: l).length) (Nat.mod_lt _ (Nat.zero_lt_succ _)) := by
  convert formPerm_pow_apply_nthLe _ h n 0 (Nat.succ_pos _)
  simp
#align list.form_perm_pow_apply_head List.formPerm_pow_apply_head

set_option linter.deprecated false in
theorem formPerm_ext_iff {x y x' y' : α} {l l' : List α} (hd : Nodup (x :: y :: l))
    (hd' : Nodup (x' :: y' :: l')) :
    formPerm (x :: y :: l) = formPerm (x' :: y' :: l') ↔ (x :: y :: l) ~r (x' :: y' :: l') :=
  by
  refine' ⟨fun h => _, fun hr => formPerm_eq_of_isRotated hd hr⟩
  rw [Equiv.Perm.ext_iff] at h
  have hx : x' ∈ x :: y :: l :=
    by
    have : x' ∈ { z | formPerm (x :: y :: l) z ≠ z } :=
      by
      rw [Set.mem_setOf_eq, h x', formPerm_apply_head _ _ _ hd']
      simp only [mem_cons, nodup_cons] at hd'
      push_neg  at hd'
      exact hd'.left.left.symm
    simpa using support_formPerm_le' _ this
  obtain ⟨n, hn, hx'⟩ := nthLe_of_mem hx
  have hl : (x :: y :: l).length = (x' :: y' :: l').length :=
    by
    rw [← dedup_eq_self.mpr hd, ← dedup_eq_self.mpr hd', ← card_toFinset, ← card_toFinset]
    refine' congr_arg Finset.card _
    rw [← Finset.coe_inj, ← support_formPerm_of_nodup' _ hd (by simp), ←
      support_formPerm_of_nodup' _ hd' (by simp)]
    simp only [h]
  use n
  apply List.ext_nthLe
  · rw [length_rotate, hl]
  · intro k hk hk'
    rw [nthLe_rotate]
    induction' k with k IH
    · refine' Eq.trans _ hx'
      congr
      simpa using hn
    · have : k.succ = (k + 1) % (x' :: y' :: l').length := by
        rw [← Nat.succ_eq_add_one, Nat.mod_eq_of_lt hk']
      simp_rw [this]
      rw [← formPerm_apply_nthLe _ hd' k (k.lt_succ_self.trans hk'), ←
        IH (k.lt_succ_self.trans hk), ← h, formPerm_apply_nthLe _ hd]
      congr 1
      have h1 : 1 = 1 % (x' :: y' :: l').length := by simp
      rw [hl, Nat.mod_eq_of_lt hk', h1, ← Nat.add_mod, Nat.succ_add, Nat.succ_eq_add_one]
#align list.form_perm_ext_iff List.formPerm_ext_iff

set_option linter.deprecated false in
theorem formPerm_apply_mem_eq_self_iff (hl : Nodup l) (x : α) (hx : x ∈ l) :
    formPerm l x = x ↔ length l ≤ 1 :=
  by
  obtain ⟨k, hk, rfl⟩ := nthLe_of_mem hx
  rw [formPerm_apply_nthLe _ hl, hl.nthLe_inj_iff]
  cases hn : l.length
  · exact absurd k.zero_le (hk.trans_le hn.le).not_le
  · rw [hn] at hk
    cases' (Nat.le_of_lt_succ hk).eq_or_lt with hk' hk'
    · simp [← hk', Nat.succ_le_succ_iff, eq_comm]
    ·
      simpa [Nat.mod_eq_of_lt (Nat.succ_lt_succ hk'), Nat.succ_lt_succ_iff] using
        k.zero_le.trans_lt hk'
#align list.form_perm_apply_mem_eq_self_iff List.formPerm_apply_mem_eq_self_iff

theorem formPerm_apply_mem_ne_self_iff (hl : Nodup l) (x : α) (hx : x ∈ l) :
    formPerm l x ≠ x ↔ 2 ≤ l.length :=
  by
  rw [Ne.def, formPerm_apply_mem_eq_self_iff _ hl x hx, not_le]
  exact ⟨Nat.succ_le_of_lt, Nat.lt_of_succ_le⟩
#align list.form_perm_apply_mem_ne_self_iff List.formPerm_apply_mem_ne_self_iff

theorem mem_of_formPerm_ne_self (l : List α) (x : α) (h : formPerm l x ≠ x) : x ∈ l :=
  by
  suffices x ∈ { y | formPerm l y ≠ y }
    by
    rw [← mem_toFinset]
    exact support_formPerm_le' _ this
  simpa using h
#align list.mem_of_form_perm_ne_self List.mem_of_formPerm_ne_self

theorem formPerm_eq_self_of_not_mem (l : List α) (x : α) (h : x ∉ l) : formPerm l x = x :=
  by_contra fun H => h <| mem_of_formPerm_ne_self _ _ H
#align list.form_perm_eq_self_of_not_mem List.formPerm_eq_self_of_not_mem

theorem formPerm_eq_one_iff (hl : Nodup l) : formPerm l = 1 ↔ l.length ≤ 1 :=
  by
  cases' l with hd tl
  · simp
  · rw [← formPerm_apply_mem_eq_self_iff _ hl hd (mem_cons_self _ _)]
    constructor
    · simp (config := { contextual := true })
    · intro h
      simp only [(hd :: tl).formPerm_apply_mem_eq_self_iff hl hd (mem_cons_self hd tl),
        add_le_iff_nonpos_left, length, nonpos_iff_eq_zero, length_eq_zero] at h
      simp [h]
#align list.form_perm_eq_one_iff List.formPerm_eq_one_iff

theorem formPerm_eq_formPerm_iff {l l' : List α} (hl : l.Nodup) (hl' : l'.Nodup) :
    l.formPerm = l'.formPerm ↔ l ~r l' ∨ l.length ≤ 1 ∧ l'.length ≤ 1 :=
  by
  rcases l with (_ | ⟨x, _ | ⟨y, l⟩⟩)
  · suffices l'.length ≤ 1 ↔ l' = nil ∨ l'.length ≤ 1 by
      simpa [eq_comm, formPerm_eq_one_iff, hl, hl', length_eq_zero]
    refine' ⟨fun h => Or.inr h, _⟩
    rintro (rfl | h)
    · simp
    · exact h
  · suffices l'.length ≤ 1 ↔ [x] ~r l' ∨ l'.length ≤ 1 by
      simpa [eq_comm, formPerm_eq_one_iff, hl, hl', length_eq_zero, le_rfl]
    refine' ⟨fun h => Or.inr h, _⟩
    rintro (h | h)
    · simp [← h.perm.length_eq]
    · exact h
  · rcases l' with (_ | ⟨x', _ | ⟨y', l'⟩⟩)
    · simp [formPerm_eq_one_iff _ hl, -formPerm_cons_cons]
    · simp [formPerm_eq_one_iff _ hl, -formPerm_cons_cons]
    · simp [-formPerm_cons_cons, formPerm_ext_iff hl hl', Nat.succ_le_succ_iff]
#align list.form_perm_eq_form_perm_iff List.formPerm_eq_formPerm_iff

theorem form_perm_zpow_apply_mem_imp_mem (l : List α) (x : α) (hx : x ∈ l) (n : ℤ) :
    (formPerm l ^ n) x ∈ l := by
  by_cases h : (l.formPerm ^ n) x = x
  · simpa [h] using hx
  · have h : x ∈ { x | (l.formPerm ^ n) x ≠ x } := h
    rw [← set_support_apply_mem] at h
    replace h := set_support_zpow_subset _ _ h
    simpa using support_formPerm_le' _ h
#align list.form_perm_zpow_apply_mem_imp_mem List.form_perm_zpow_apply_mem_imp_mem

set_option linter.deprecated false in
theorem formPerm_pow_length_eq_one_of_nodup (hl : Nodup l) : formPerm l ^ length l = 1 := by
  ext x
  by_cases hx : x ∈ l
  · obtain ⟨k, hk, rfl⟩ := nthLe_of_mem hx
    simp [formPerm_pow_apply_nthLe _ hl, Nat.mod_eq_of_lt hk]
  · have : x ∉ { x | (l.formPerm ^ l.length) x ≠ x } :=
      by
      intro H
      refine' hx _
      replace H := set_support_zpow_subset l.formPerm l.length H
      simpa using support_formPerm_le' _ H
    simpa using this
#align list.form_perm_pow_length_eq_one_of_nodup List.formPerm_pow_length_eq_one_of_nodup

end FormPerm

end List


-- Isomorfismo_entre_relaciones_de_equivalencia_y_particiones.lean
-- Isomorfismo entre relaciones de equivalencia y particiones
-- José A. Alonso Jiménez
-- Sevilla, 15 de octubre de 2021
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Demostrar que los tipos de las relaciones de equivalencia sobre A y
-- el de las particiones de A son isomorfos.
-- ---------------------------------------------------------------------

import tactic

@[ext] structure particion (A : Type) :=
(Bloques    : set (set A))
(Hno_vacios : ∀ X ∈ Bloques, (X : set A).nonempty)
(Hrecubren  : ∀ a, ∃ X ∈ Bloques, a ∈ X)
(Hdisjuntos : ∀ X Y ∈ Bloques, (X ∩ Y : set A).nonempty → X = Y)

namespace particion

variable  {A : Type}
variables {X Y : set A}
variable  {P : particion A}
variable  (R : A → A → Prop)

def clase (a : A) :=
  {b : A | R b a}

def clases : (A → A → Prop) → set (set A) :=
  λ R, {B : set A | ∃ x : A, B = clase R x}

lemma pertenece_clase_syss
  {a b : A}
  : b ∈ clase R a ↔ R b a :=
by refl

lemma clases_no_vacias
  (hR: equivalence R)
  : ∀ (X : set A), X ∈ clases R → X.nonempty :=
begin
  rintros _ ⟨a, rfl⟩,
  use a,
  rw pertenece_clase_syss,
  apply hR.1,
end

lemma clases_recubren
  (hR: equivalence R)
  : ∀ a, ∃ X ∈ clases R, a ∈ X :=
begin
  intro a,
  use clase R a,
  split,
  { use a, },
  { exact hR.1 a, },
end

lemma subclase_si_pertenece
  {R : A → A → Prop}
  (hR: equivalence R)
  {a b : A}
  : a ∈ clase R b → clase R a ⊆ clase R b :=
λ hab z hza, hR.2.2 hza hab

lemma clases_iguales_si_pertenece
  {R : A → A → Prop}
  (hR: equivalence R)
  {a b : A}
  : a ∈ clase R b → clase R a = clase R b :=
λ hab, set.subset.antisymm
        (subclase_si_pertenece hR hab)
        (subclase_si_pertenece hR (hR.2.1 hab))

lemma clases_disjuntas
  (hR: equivalence R)
  : ∀ X Y ∈ clases R, (X ∩ Y : set A).nonempty → X = Y :=
begin
  rintros X ⟨a, rfl⟩ Y ⟨b, rfl⟩ ⟨c, hca, hcb⟩,
  exact clases_iguales_si_pertenece hR (hR.2.2 (hR.2.1 hca) hcb),
end

def cociente : {R : A → A → Prop // equivalence R} → particion A :=
  λ R, { Bloques    := {B : set A | ∃ x : A, B = clase R.1 x},
         Hno_vacios := clases_no_vacias R.1 R.2,
         Hrecubren  := clases_recubren R.1 R.2,
         Hdisjuntos := clases_disjuntas R.1 R.2, }

def relacion : (particion A) → (A → A → Prop) :=
  λ P a b, ∀ X ∈ Bloques P, a ∈ X → b ∈ X

lemma reflexiva
  (P : particion A)
  : reflexive (relacion P) :=
λ a X hXC haX, haX

lemma iguales_si_comun
  (hX : X ∈ Bloques P)
  (hY : Y ∈ Bloques P)
  {a : A}
  (haX : a ∈ X)
  (haY : a ∈ Y)
  : X = Y :=
Hdisjuntos P X hX Y hY ⟨a, haX, haY⟩

lemma pertenece_si_pertenece
  (hX : X ∈ Bloques P)
  (hY : Y ∈ Bloques P)
  {a b : A}
  (haX : a ∈ X)
  (haY : a ∈ Y)
  (hbX : b ∈ X)
  : b ∈ Y :=
begin
  convert hbX,
  exact iguales_si_comun hY hX haY haX,
end

lemma simetrica
  (P : particion A)
  : symmetric (relacion P) :=
begin
  intros a b h X hX hbX,
  obtain ⟨Y, hY, haY⟩ := Hrecubren P a,
  specialize h Y hY haY,
  exact pertenece_si_pertenece hY hX h hbX haY,
end

lemma transitiva
  (P : particion A)
  : transitive (relacion P) :=
λ a b c hab hbc X hX haX, hbc X hX (hab X hX haX)

def relacionP : particion A → {R : A → A → Prop // equivalence R} :=
  λ P, ⟨λ a b, ∀ X ∈ Bloques P, a ∈ X → b ∈ X,
        ⟨reflexiva P, simetrica P, transitiva P⟩⟩

lemma inversa_izq :
  function.left_inverse relacionP (@cociente A) :=
begin
  rintro ⟨R, hR⟩,
  simp [relacionP, cociente],
  ext a b,
  exact ⟨λ hab, hR.2.1 (hab a (hR.1 a)),
         λ hab c hac, hR.2.2 (hR.2.1 hab) hac⟩,
end

lemma inversa_dcha :
  function.right_inverse relacionP (@cociente A) :=
begin
  intro P,
  ext X,
  show (∃ (a : A), X = clase _ a) ↔ X ∈ Bloques P,
  split,
  { rintro ⟨a, rfl⟩,
    obtain ⟨X, hX, haX⟩ := Hrecubren P a,
    convert hX,
    ext b,
    rw pertenece_clase_syss,
    split,
    { intro hba,
      obtain ⟨Y, hY, hbY⟩ := Hrecubren P b,
      specialize hba Y hY hbY,
      convert hbY,
      exact iguales_si_comun hX hY haX hba, },
    { intros hbX Y hY hbY,
      apply pertenece_si_pertenece hX hY hbX hbY haX, }},
  { intro hX,
    rcases Hno_vacios P X hX with ⟨a, ha⟩,
    use a,
    ext b,
    split,
    { intro hbX,
      rw pertenece_clase_syss,
      intros Y hY hbY,
      exact pertenece_si_pertenece hX hY hbX hbY ha, },
    { rw pertenece_clase_syss,
      intro hba,
      obtain ⟨Y, hY, hbY⟩ := Hrecubren P b,
      specialize hba Y hY hbY,
      exact pertenece_si_pertenece hY hX hba ha hbY, }}
end

noncomputable theorem equivalencia_particiones
  (A : Type)
  : {R : A → A → Prop // equivalence R} ≃ particion A :=
{ to_fun    := cociente,
  inv_fun   := relacionP,
  left_inv  := inversa_izq,
  right_inv := inversa_dcha, }

end particion


/-
Copyright (c) 2022 Frédéric Dupuis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Frédéric Dupuis
-/

import topology.algebra.module.character_space
import analysis.normed_space.weak_dual
import analysis.normed_space.spectrum

/-!
# Normed algebras

This file contains basic facts about normed algebras.

## Main results

* We show that the character space of a normed algebra is compact using the Banach-Alaoglu theorem.

## TODO

* Show compactness for topological vector spaces; this requires the TVS version of Banach-Alaoglu.

## Tags

normed algebra, character space, continuous functional calculus

-/

variables {𝕜 : Type*} {A : Type*}

namespace weak_dual
namespace character_space

variables [nondiscrete_normed_field 𝕜] [normed_ring A]
  [normed_algebra 𝕜 A] [complete_space A] [norm_one_class A]

lemma norm_one (φ : character_space 𝕜 A) : ∥to_normed_dual (φ : weak_dual 𝕜 A)∥ = 1 :=
begin
  refine continuous_linear_map.op_norm_eq_of_bounds zero_le_one (λ a, _) (λ x hx h, _),
  { rw [one_mul],
    exact spectrum.norm_le_norm_of_mem (apply_mem_spectrum φ a) },
  { have : ∥φ 1∥ ≤ x * ∥(1 : A)∥ := h 1,
    simpa only [norm_one, mul_one, map_one] using this },
end

instance [proper_space 𝕜] : compact_space (character_space 𝕜 A) :=
begin
  rw [←is_compact_iff_compact_space],
  have h : character_space 𝕜 A ⊆ to_normed_dual ⁻¹' metric.closed_ball 0 1,
  { intros φ hφ,
    rw [set.mem_preimage, mem_closed_ball_zero_iff],
    exact (le_of_eq $ norm_one ⟨φ, ⟨hφ.1, hφ.2⟩⟩ : _), },
  exact compact_of_is_closed_subset (is_compact_closed_ball 𝕜 0 1) is_closed h,
end

end character_space
end weak_dual


/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura

! This file was ported from Lean 3 source module lean_core.data.vector
! leanprover-community/lean commit 855e5b74e3a52a40552e8f067169d747d48743fd
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Mathport.Rename
import Std.Data.List.Basic
import Std.Data.List.Lemmas
import Mathlib.Init.Data.List.Basic
import Mathlib.Init.Data.List.Lemmas

/-!
The type `Vector` represents lists with fixed length.
-/

universe u v w
/-- `Vector α n` is the type of lists of length `n` with elements of type `α`. -/
def Vector (α : Type u) (n : ℕ) :=
  { l : List α // l.length = n }
#align vector Vector

namespace Vector

variable {α : Type u} {β : Type v} {φ : Type w}

variable {n : ℕ}

instance [DecidableEq α] : DecidableEq (Vector α n) :=
  inferInstanceAs (DecidableEq {l : List α // l.length = n})

/-- The empty vector with elements of type `α` -/
@[match_pattern]
def nil : Vector α 0 :=
  ⟨[], rfl⟩
#align vector.nil Vector.nil

/-- If `a : α` and `l : Vector α n`, then `cons a l`, is the vector of length `n + 1`
whose first element is a and with l as the rest of the list. -/
@[match_pattern]
def cons : α → Vector α n → Vector α (Nat.succ n)
  | a, ⟨v, h⟩ => ⟨a :: v, congrArg Nat.succ h⟩
#align vector.cons Vector.cons

/-- The length of a vector. -/
@[reducible, nolint unusedArguments]
def length (_ : Vector α n) : ℕ :=
  n
#align vector.length Vector.length

open Nat

/-- The first element of a vector with length at least `1`. -/
def head : Vector α (Nat.succ n) → α
  | ⟨[], h⟩ => by contradiction
  | ⟨a :: _, _⟩ => a
#align vector.head Vector.head

/-- The head of a vector obtained by prepending is the element prepended. -/
theorem head_cons (a : α) : ∀ v : Vector α n, head (cons a v) = a
  | ⟨_, _⟩ => rfl
#align vector.head_cons Vector.head_cons

/-- The tail of a vector, with an empty vector having empty tail.  -/
def tail : Vector α n → Vector α (n - 1)
  | ⟨[], h⟩ => ⟨[], congrArg pred h⟩
  | ⟨_ :: v, h⟩ => ⟨v, congrArg pred h⟩
#align vector.tail Vector.tail

/-- The tail of a vector obtained by prepending is the vector prepended. to -/
theorem tail_cons (a : α) : ∀ v : Vector α n, tail (cons a v) = v
  | ⟨_, _⟩ => rfl
#align vector.tail_cons Vector.tail_cons

/-- Prepending the head of a vector to its tail gives the vector. -/
@[simp]
theorem cons_head_tail : ∀ v : Vector α (succ n), cons (head v) (tail v) = v
  | ⟨[], h⟩ => by contradiction
  | ⟨a :: v, h⟩ => rfl
#align vector.cons_head_tail Vector.cons_head_tail

/-- The list obtained from a vector. -/
def toList (v : Vector α n) : List α :=
  v.1
#align vector.to_list Vector.toList

-- porting notes: align to `List` API
/-- nth element of a vector, indexed by a `Fin` type. -/
def get : ∀ _ : Vector α n, Fin n → α
  | ⟨l, h⟩, i => l.nthLe i.1 (by rw [h] ; exact i.2)
#align vector.nth Vector.get

/-- Appending a vector to another. -/
def append {n m : Nat} : Vector α n → Vector α m → Vector α (n + m)
  | ⟨l₁, h₁⟩, ⟨l₂, h₂⟩ => ⟨l₁ ++ l₂, by simp [*]⟩
#align vector.append Vector.append

/- warning: vector.elim -> Vector.elim is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {C : forall {n : ℕ},
  (Vector.{u_1} α n) -> Sort.{u}}, (forall (l : List.{u_1} α),
  C (List.length.{u_1} α l) (Subtype.mk.{succ u_1} (List.{u_1} α)
  (fun (l_1 : List.{u_1} α) => Eq.{1} ℕ (List.length.{u_1} α l_1)
  (List.length.{u_1} α l)) l (Vector.Elim._proof_1.{u_1} α l))) ->
  (forall {n : ℕ} (v : Vector.{u_1} α n), C n v)
but is expected to have type
  forall {α : Type.{_aux_param_0}} {C : forall {n : ℕ}, (Vector.{_aux_param_0} α n) -> Sort.{u}},
  (forall (l : List.{_aux_param_0} α),
  C (List.length.{_aux_param_0} α l) (Subtype.mk.{succ _aux_param_0} (List.{_aux_param_0} α)
  (fun (l_1 : List.{_aux_param_0} α) => Eq.{1} ℕ (List.length.{_aux_param_0} α l_1)
  (List.length.{_aux_param_0} α l)) l (rfl.{1} ℕ (List.length.{_aux_param_0} α l)))) ->
  (forall {n : ℕ} (v : Vector.{_aux_param_0} α n), C n v)
Case conversion may be inaccurate. Consider using '#align vector.elim Vector.elimₓ'. -/
/-- Elimination rule for `Vector`. -/
@[elab_as_elim]
def elim {α} {C : ∀ {n}, Vector α n → Sort u}
    (H : ∀ l : List α, C ⟨l, rfl⟩) {n : ℕ} : ∀ v : Vector α n, C v
  | ⟨l, h⟩ =>
    match n, h with
    | _, rfl => H l
#align vector.elim Vector.elim

/-- Map a vector under a function. -/
def map (f : α → β) : Vector α n → Vector β n
  | ⟨l, h⟩ => ⟨List.map f l, by simp [*]⟩
#align vector.map Vector.map

/-- A `nil` vector maps to a `nil` vector. -/
@[simp]
theorem map_nil (f : α → β) : map f nil = nil :=
  rfl
#align vector.map_nil Vector.map_nil

/-- `map` is natural with respect to `cons`. -/
theorem map_cons (f : α → β) (a : α) : ∀ v : Vector α n, map f (cons a v) = cons (f a) (map f v)
  | ⟨_, _⟩ => rfl
#align vector.map_cons Vector.map_cons

/-- Mapping two vectors under a curried function of two variables. -/
def map₂ (f : α → β → φ) : Vector α n → Vector β n → Vector φ n
  | ⟨x, _⟩, ⟨y, _⟩ => ⟨List.zipWith f x y, by simp [*]⟩
#align vector.map₂ Vector.map₂

/-- Vector obtained by repeating an element. -/
def replicate (n : ℕ) (a : α) : Vector α n :=
  ⟨List.replicate n a, List.length_replicate n a⟩
#align vector.replicate Vector.replicate

/-- Drop `i` elements from a vector of length `n`; we can have `i > n`. -/
def drop (i : ℕ) : Vector α n → Vector α (n - i)
  | ⟨l, p⟩ => ⟨List.drop i l, by simp [*]⟩
#align vector.drop Vector.drop

/-- Take `i` elements from a vector of length `n`; we can have `i > n`. -/
def take (i : ℕ) : Vector α n → Vector α (min i n)
  | ⟨l, p⟩ => ⟨List.take i l, by simp [*]⟩
#align vector.take Vector.take

/-- Remove the element at position `i` from a vector of length `n`. -/
def removeNth (i : Fin n) : Vector α n → Vector α (n - 1)
  | ⟨l, p⟩ => ⟨List.removeNth l i.1, by rw [l.length_removeNth] <;> rw [p] ; exact i.2⟩
#align vector.remove_nth Vector.removeNth

/-- Vector of length `n` from a function on `Fin n`. -/
def ofFn : ∀ {n}, (Fin n → α) → Vector α n
  | 0, _ => nil
  | _ + 1, f => cons (f 0) (ofFn fun i ↦ f i.succ)
#align vector.of_fn Vector.ofFn

section Accum

open Prod

variable {σ : Type}

/-- Runs a function over a vector returning the intermediate results and a
a final result.
-/
def mapAccumr (f : α → σ → σ × β) : Vector α n → σ → σ × Vector β n
  | ⟨x, px⟩, c =>
    let res := List.mapAccumr f x c
    ⟨res.1, res.2, by simp [*]⟩
#align vector.map_accumr Vector.mapAccumr

/-- Runs a function over a pair of vectors returning the intermediate results and a
a final result.
-/
def mapAccumr₂ {α β σ φ : Type} (f : α → β → σ → σ × φ) :
    Vector α n → Vector β n → σ → σ × Vector φ n
  | ⟨x, px⟩, ⟨y, py⟩, c =>
    let res := List.mapAccumr₂ f x y c
    ⟨res.1, res.2, by simp [*]⟩
#align vector.map_accumr₂ Vector.mapAccumr₂

end Accum

/-- Vector is determined by the underlying list. -/
protected theorem eq {n : ℕ} : ∀ a1 a2 : Vector α n, toList a1 = toList a2 → a1 = a2
  | ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
#align vector.eq Vector.eq

/-- A vector of length `0` is a `nil` vector. -/
protected theorem eq_nil (v : Vector α 0) : v = nil :=
  v.eq nil (List.eq_nil_of_length_eq_zero v.2)
#align vector.eq_nil Vector.eq_nil

/-- Vector of length from a list `v`
with witness that `v` has length `n` maps to `v` under `toList`.  -/
@[simp]
theorem toList_mk (v : List α) (P : List.length v = n) : toList (Subtype.mk v P) = v :=
  rfl
#align vector.to_list_mk Vector.toList_mk

/-- A nil vector maps to a nil list. -/
@[simp, nolint simpNF] -- Porting note: simp can prove this in the future
theorem toList_nil : toList nil = @List.nil α :=
  rfl
#align vector.to_list_nil Vector.toList_nil

/-- The length of the list to which a vector of length `n` maps is `n`. -/
@[simp]
theorem toList_length (v : Vector α n) : (toList v).length = n :=
  v.2
#align vector.to_list_length Vector.toList_length

/-- `toList` of `cons` of a vector and an element is
the `cons` of the list obtained by `toList` and the element -/
@[simp]
theorem toList_cons (a : α) (v : Vector α n) : toList (cons a v) = a :: toList v := by
  cases v ; rfl
#align vector.to_list_cons Vector.toList_cons

/-- Appending of vectors corresponds under `toList` to appending of lists. -/
@[simp]
theorem toList_append {n m : ℕ} (v : Vector α n) (w : Vector α m) :
   toList (append v w) = toList v ++ toList w := by
  cases v
  cases w
  rfl
#align vector.to_list_append Vector.toList_append

/-- `drop` of vectors corresponds under `toList` to `drop` of lists. -/
@[simp]
theorem toList_drop {n m : ℕ} (v : Vector α m) : toList (drop n v) = List.drop n (toList v) := by
  cases v
  rfl
#align vector.to_list_drop Vector.toList_drop

/-- `take` of vectors corresponds under `toList` to `take` of lists. -/
@[simp]
theorem toList_take {n m : ℕ} (v : Vector α m) : toList (take n v) = List.take n (toList v) := by
  cases v
  rfl
#align vector.to_list_take Vector.toList_take

end Vector


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

! This file was ported from Lean 3 source module data.finset.order
! 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.Finset.Basic

/-!
# Finsets of ordered types
-/

universe u v w

variable {α : Type u}

theorem Directed.finset_le {r : α → α → Prop} [IsTrans α r] {ι} [hι : Nonempty ι] {f : ι → α}
    (D : Directed r f) (s : Finset ι) : ∃ z, ∀ i ∈ s, r (f i) (f z) :=
  show ∃ z, ∀ i ∈ s.1, r (f i) (f z) from
    Multiset.induction_on s.1 (let ⟨z⟩ := hι; ⟨z, fun _ ↦ by simp⟩)
      fun i s ⟨j, H⟩ ↦
      let ⟨k, h₁, h₂⟩ := D i j
      ⟨k, fun a h ↦ (Multiset.mem_cons.1 h).casesOn (fun h ↦ h.symm ▸ h₁)
        fun h ↦ _root_.trans (H _ h) h₂⟩
#align directed.finset_le Directed.finset_le

theorem Finset.exists_le [Nonempty α] [Preorder α] [IsDirected α (· ≤ ·)] (s : Finset α) :
    ∃ M, ∀ i ∈ s, i ≤ M :=
  directed_id.finset_le _
#align finset.exists_le Finset.exists_le


/-
Copyright (c) 2022 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Kevin Buzzard
-/

import tactic -- imports all the Lean tactics
import data.real.basic -- imports the real numbers
import solutions.section02reals.sheet5 -- import a bunch of previous stuff

/-

# Harder questions

Here are some harder questions. Don't feel like you have
to do them. We've seen enough techniques to be able to do
all of these, but the truth is that we've seen a ton of stuff
in this course already, so probably you're not on top of all of
it yet, and furthermore we have not seen
some techniques which will enable you to cut corners. If you
want to become a real Lean expert then see how many of these
you can do. I will go through them all in a solutions video,
so if you like you can try some of them and then watch me
solving them.

Good luck!
-/

/-- If `a(n)` tends to `t` then `37 * a(n)` tends to `37 * t`-/
theorem tendsto_thirtyseven_mul (a : ℕ → ℝ) (t : ℝ) (h : tendsto a t) :
  tendsto (λ n, 37 * a n) (37 * t) :=
begin
  sorry,
end

/-- If `a(n)` tends to `t` and `c` is a positive constant then
`c * a(n)` tends to `c * t`. -/
theorem tendsto_pos_const_mul {a : ℕ → ℝ} {t : ℝ} (h : tendsto a t)
  {c : ℝ} (hc : 0 < c) : tendsto (λ n, c * a n) (c * t) :=
begin
  sorry,
end

/-- If `a(n)` tends to `t` and `c` is a negative constant then
`c * a(n)` tends to `c * t`. -/
theorem tendsto_neg_const_mul {a : ℕ → ℝ} {t : ℝ} (h : tendsto a t)
  {c : ℝ} (hc : c < 0) : tendsto (λ n, c * a n) (c * t) :=
begin
  sorry,
end

/-- If `a(n)` tends to `t` and `c` is a constant then `c * a(n)` tends
to `c * t`. -/
theorem tendsto_const_mul {a : ℕ → ℝ} {t : ℝ} (c : ℝ) (h : tendsto a t) :
  tendsto (λ n, c * a n) (c * t) :=
begin
  sorry,
end

/-- If `a(n)` tends to `t` and `c` is a constant then `a(n) * c` tends
to `t * c`. -/
theorem tendsto_mul_const {a : ℕ → ℝ} {t : ℝ} (c : ℝ) (h : tendsto a t) :
  tendsto (λ n, a n * c) (t * c) :=
begin
  sorry
end

-- another proof of this result, showcasing some tactics
-- which I've not covered yet.
theorem tendsto_neg' {a : ℕ → ℝ} {t : ℝ} (ha : tendsto a t) :
  tendsto (λ n, - a n) (-t) :=
begin
  convert tendsto_const_mul (-1) ha, -- read about `convert` on the community web pages
  { ext, simp }, -- ext is a generic extensionality tactic. Here it's being
                 -- used to deduce that two functions are the same if they take
                 -- the same values everywhere
  { simp },
end

/-- If `a(n)-b(n)` tends to `t` and `b(n)` tends to `u` then
`a(n)` tends to `t + u`. -/
theorem tendsto_of_tendsto_sub {a b : ℕ → ℝ} {t u : ℝ}
  (h1 : tendsto (λ n, a n - b n) t) (h2 : tendsto b u) :
  tendsto a (t+u) :=
begin
  sorry,
end

/-- If `a(n)` tends to `t` then `a(n)-t` tends to `0`. -/
theorem tendsto_sub_lim {a : ℕ → ℝ} {t : ℝ}
  (h : tendsto a t) : tendsto (λ n, a n - t) 0 :=
begin
  sorry,
end

/-- If `a(n)` and `b(n)` both tend to zero, then their product tends
to zero. -/
theorem tendsto_zero_mul_tendsto_zero
  {a b : ℕ → ℝ} (ha : tendsto a 0) (hb : tendsto b 0) :
  tendsto (λ n, a n * b n) 0 :=
begin
  sorry,
end

/-- If `a(n)` tends to `t` and `b(n)` tends to `u` then
`a(n)*b(n)` tends to `t*u`. -/
theorem tendsto_mul (a b : ℕ → ℝ) (t u : ℝ) (ha : tendsto a t)
  (hb : tendsto b u) : tendsto (λ n, a n * b n) (t * u) :=
begin
  sorry,
end

-- something we never used!
/-- A sequence has at most one limit. -/
theorem tendsto_unique (a : ℕ → ℝ) (s t : ℝ)
  (hs : tendsto a s) (ht : tendsto a t) : s = t :=
begin
  sorry,
end


import data.num.lemmas

namespace nat

lemma eq_zero_of_le {n : ℕ}
  (h : n ≤ 0)
: n = 0 :=
by { apply le_antisymm h, apply zero_le }

theorem lt_of_not_le {m n : ℕ} (h : ¬ m ≤ n) : n < m :=
begin
  cases lt_or_ge n m with h' h',
  { apply h' },
  { cases h h' }
end

theorem mod_of_lt {m n : ℕ} (h : n > m) : m % n = m :=
begin
  rw [nat.mod_def,if_neg],
  intro h',
  cases h' with h₀ h₁,
  apply not_le_of_gt h h₁,
end

theorem mod_plus (n p : ℕ) (h : p > 0) : ∃k q, q < p ∧ n = k * p + q :=
begin
  existsi (n / p), existsi (n % p),
  split,
  { apply mod_lt _ h, },
  { simp [mod_add_div,mul_comm,mod_add_div], }
end

theorem mul_plus_mod (k p q : ℕ) (h : q < p) : (k * p + q) % p = q :=
begin
  induction k with k,
  { rw [mod_def,if_neg], simp,
    simp, intro h', assumption },
  { rw [mod_def,if_pos,succ_mul],
    simp [nat.add_sub_cancel_left],
    simp at k_ih,
    apply k_ih,
    { split,
      { apply lt_of_le_of_lt (zero_le _), apply h },
      { simp [succ_mul]  } } }
end

theorem succ_mod' {n p : ℕ} (h : succ (n % p) < p) : succ n % p = succ (n % p) :=
begin
   have h₁ : p > 0,
   { apply lt_of_le_of_lt (zero_le _), apply h },
   have h₀ := mod_plus n p h₁,
   cases h₀ with k h₀, cases h₀ with q h₀,
   cases h₀ with h₀ h₁,
   subst n,
   rw mul_plus_mod _ _ _ h₀ at h,
   rw [← add_succ,mul_plus_mod _ _ _ h₀,mul_plus_mod _ _ _ h],
end

theorem succ_mod (n p : ℕ) : succ n % p = succ (n % p) % p :=
begin
  symmetry,
  cases decidable.em (0 < p) with h₂ h₂,
  cases decidable.em (p ≤ succ (n % p)) with h₃ h₃,
  { have h' : succ (n % p) = p,
    { apply le_antisymm _ h₃,
      { apply mod_lt _ h₂ } },
    cases mod_plus n p h₂  with k h₀,
    cases h₀ with q h₀,
    cases h₀ with h₀ h₁,
    rw [mod_def,if_pos (and.intro h₂ h₃),h',nat.sub_self,zero_mod],
    subst n, rw [mul_plus_mod _ _ _ h₀] at h',
    have h₁ : k * p + succ q = (k+1) * p + 0,
    { rw h', simp [add_mul] },
    rw [← add_succ, h₁, mul_plus_mod _ _ _ h₂], },
  { have h₀ := lt_of_not_ge h₃,
    rw [succ_mod' h₀,succ_mod',mod_mod],
    { rw mod_mod, apply h₀ }  },
  { have h'' := le_antisymm (zero_le _) (le_of_not_gt h₂),
    subst p, simp [nat.mod_zero] },
end

theorem mod_add' {m n p : ℕ} : (m + n) % p = (m + n % p) % p :=
begin
  induction m with m,
  { symmetry, simp },
  { rw [succ_add,succ_mod,m_ih,succ_add,← succ_mod] }
end

theorem mod_add {m n p : ℕ} : (m + n) % p = (m % p + n % p) % p :=
begin
  rw [mod_add',nat.add_comm,mod_add',nat.add_comm]
end

theorem nat.mod_self' {m : ℕ} : m % m = 0 :=
begin
  rw [mod_def],
  cases decidable.em (0 < m) with h h,
  { have h' := and.intro h (nat.le_refl m),
    rw [if_pos h',nat.sub_self,zero_mod] },
  { have h' : ¬ (0 < m ∧ m ≤ m),
    { intro h', apply h,
      cases h' with h₀ h₁,
      apply h₀ },
    rw [if_neg h'],
    apply le_antisymm _ (zero_le _),
    apply le_of_not_gt h },
end

theorem lt_of_pred_lt {m n : ℕ}
  (h : m < pred n)
: m < n :=
begin
  cases n with n
  ; unfold pred at h,
  { apply h },
  { apply nat.le_trans h (le_succ _), },
end

theorem lt_pred_of_lt {m n : ℕ}
  (h : m < n)
: pred m < n :=
begin
  cases m with m,
  { apply h },
  { apply nat.lt_trans _ h,
    apply lt_succ_self }
end

theorem succ_pred_le_self (x : ℕ)
: x ≤ succ (pred x) :=
begin
  cases x with x,
  { apply zero_le },
  { apply succ_le_succ,
    refl },
end

lemma pred_lt_self_of_pos : ∀ {v : ℕ},
  0 < v → pred v < v
| 0 p := absurd p (not_lt_zero _)
| (succ n) _ := lt_succ_self _

protected lemma sub_lt_sub_right {m n p : ℕ}
  (h₀ : p ≤ m)
  (h₁ : m < n)
: m - p < n - p :=
begin
  induction p with p IH,
  { apply h₁ },
  { simp [sub_succ],
    have Hmp : m - p ≠ 0,
    { apply ne_of_gt,
      apply nat.sub_pos_of_lt h₀ },
    apply pred_lt_pred Hmp,
    apply IH,
    apply le_trans _ h₀,
    apply le_succ }
end

protected lemma sub_lt_sub_left {m p q : ℕ}
  (h₀ : p ≤ m)
  (h₁ : q < p)
: m - p < m - q :=
begin
  apply lt_of_succ_le,
  rw ← succ_sub h₀,
  cases p with p,
  { cases not_lt_zero _ h₁ },
  { rw succ_sub_succ,
    apply nat.sub_le_sub_left,
    apply le_of_succ_le_succ h₁ },
end

protected lemma sub_le_sub {m n p q : ℕ}
  (h₀ : m ≤ n)
  (h₁ : q ≤ p)
: m - p ≤ n - q :=
begin
  transitivity n - p,
  apply nat.sub_le_sub_right h₀,
  apply nat.sub_le_sub_left _ h₁
end

protected lemma mul_add_mod_self_left
  (x y k : ℕ)
  (h : k < x)
: (x * y + k) % x = k :=
by simp [mod_eq_of_lt h]

protected lemma mul_add_div_self_left (x y k : ℕ) (h : k < x)
: (x * y + k) / x = y :=
begin
  have h₀ : x > 0,
  { apply lt_of_le_of_lt (zero_le _) h },
  simp,
  induction y with y,
  { simp [div_eq_of_lt h] },
  { rw [mul_succ,div_eq_sub_div h₀],
    { simp [nat.add_sub_cancel_left,add_one,y_ih] },
    { simp } }
end

protected lemma add_mul_div_self_right {a : ℕ} (b : ℕ) {n : ℕ} (h : a < n)
: (a + b * n) / n = b :=
by rw [add_comm,mul_comm,nat.mul_add_div_self_left _ _ _ h]

protected lemma mul_lt_mul {a b c d : ℕ}
  (h₀ : a < c)
  (h₁ : b < d)
: a * b < c * d :=
begin
  revert a,
  induction c with c ; intros a h₀,
  { cases (nat.not_lt_zero _ h₀) },
  cases a with a,
  { rw [nat.succ_mul],
    apply lt_of_lt_of_le,
    simp, apply lt_of_le_of_lt (zero_le b), apply h₁,
    apply le_add_left  },
  { rw [succ_mul,succ_mul],
    apply add_lt_add,
    apply c_ih,
    apply lt_of_succ_lt_succ h₀,
    apply h₁, }
end

lemma sub_succ_eq_pred_sub (x y : ℕ)
: x - succ y = pred x - y :=
begin
  induction y,
  { refl },
  { unfold has_sub.sub nat.sub,
    apply congr_arg, apply y_ih }
end

end nat


def f (x : Nat) (y : Bool) :=
x + if y then 1 else 0

def g (x y : Nat) :=
x + y

#check f ?x ?x -- error the first occurrence (?x : Nat) and the second (?x : Bool)

#check g ?x ?x -- ok

def h1 (x : Nat) : Nat := by
refine g ?hole ?hole; -- it is the same hole
case hole => exact x

#eval h1 10

theorem ex1 : h1 10 = 20 :=
rfl

def h2 (x : Nat) : Nat := by
refine g ?hole ?hole;
exact x+x

theorem ex2 : h2 10 = 40 :=
rfl

def foo (f : Nat → Nat) (x : Nat) := f x
def bla (x : Nat) (f : Nat → Nat) := f x
def boo (f : Nat → Nat) (g : Bool → Nat) := f (g true)

#check foo (fun x => ?hole) ?hole
#check bla ?hole (fun x => ?hole)
#check boo (fun x => ?hole) (fun y => ?hole) -- error the local contexts of the two holes are incompatible

def h3 (x : Nat) : Nat := by
apply boo;
case f => refine fun y => ?hole + 1; exact x;  -- `fun y => ?hole + 1` and assigned `?hole := x`
case g => refine fun b => ?hole                -- `fun b => ?hole` it works because assignment is compatible

#eval h3 10

theorem ex3 : h3 10 = 11 := rfl

def h4 (x : Nat) : Nat := by
refine foo (fun y => ?hole + 2) ?hole;
-- note that the local context of ?hole has be shrunk by the second occurrence
exact x

#eval h4 10

theorem ex4 : h4 10 = 12 := rfl

def h5 (x : Nat) : Nat := by
apply boo;
case f => intro y; refine ?hole + 1; exact y;  -- `fun y => ?hole + 1` and assigned `?hole := y`
case g => refine fun b => ?hole                -- error


import data.set.lattice
import data.nat.parity
import tactic

section
variable {α : Type*}
variables (s t u : set α)

open set

example (h : s ⊆ t) : s ∩ u ⊆ t ∩ u :=
begin
  rw [subset_def, inter_def, inter_def],
  rw subset_def at h,
  dsimp,
  rintros x ⟨xs, xu⟩,
  from ⟨h x xs, xu⟩,
end

example (h : s ⊆ t) : s ∩ u ⊆ t ∩ u :=
begin
  simp only [subset_def, mem_inter_eq] at *,
  rintros x ⟨xs, xu⟩,
  from ⟨h x xs, xu⟩,
end

example (h : s ⊆ t) : s ∩ u ⊆ t ∩ u :=
begin
  rintros x ⟨xs, xu⟩,
  from ⟨h xs, xu⟩,
end

example : s ∩ (t ∪ u) ⊆ (s ∩ t) ∪ (s ∩ u) :=
begin
  rintros x ⟨hs, htu⟩,
  cases htu with ht hu,
  { left,
    from ⟨hs, ht⟩,},
  { right,
    from ⟨hs, hu⟩,}
end

example : (s ∩ t) ∪ (s ∩ u) ⊆ s ∩ (t ∪ u):=
begin
  rintros x (⟨hs, ht⟩ | ⟨hs, hu⟩),
  { from ⟨hs, or.inl ht⟩,},
  { from ⟨hs, or.inr hu⟩,},
end

example : s \ t \ u ⊆ s \ (t ∪ u) :=
begin
  rintros x ⟨⟨hs, hnt⟩, hnu⟩,
  split,
  { from hs},
  { dsimp,
    rintro (ht | hu),
    { from hnt ht,},
    { from hnu hu},},
end

example : s \ t \ u ⊆ s \ (t ∪ u) :=
begin
  rintros x ⟨⟨xs, xnt⟩, xnu⟩,
  use xs,
  rintros (xt | xu); contradiction,
end

example : s \ (t ∪ u) ⊆ s \ t \ u :=
begin
  rintros x ⟨xs, xsu⟩,
  dsimp at xsu,
  use xs,
  intro xt,
  from xsu (or.inl xt),
  intro xu,
  from xsu (or.inr xu),
end

example : s ∩ t = t ∩ s :=
begin
  ext x,
  simp only [mem_inter_eq],
  split,
  { rintros ⟨xs, xt⟩, exact ⟨xt, xs⟩,},
  { rintros ⟨xt, xs⟩, exact ⟨xs, xt⟩},
end

example : s ∩ t = t ∩ s :=
subset.antisymm (λ x ⟨xs, xt⟩, ⟨xt, xs⟩) (λ x ⟨xt, xs⟩, ⟨xs, xt⟩)

example : s ∩ (s ∪ t) = s :=
begin
  ext x,
  dsimp,
  split,
  { intro xsst,
    from xsst.1,},
  { intro xs,
    from ⟨xs, or.inl xs⟩,},
end

example : s ∪ (s ∩ t) = s :=
begin
  ext x,
  dsimp,
  split,
  { rintro (xs | ⟨xs, xt⟩);
    from xs,},
  { intro xs,
    left,
    from xs,},
end

example : (s \ t) ∪ t = s ∪ t :=
begin
  ext x,
  split,
  { rintro (⟨xs, xnt⟩ | xt),
    { from or.inl xs,},
    { from or.inr xt,},},
  { by_cases h : x ∈ t,
    { intro _,
      right,
      from h,},
    { rintro (xs | xt),
      { left,
        from ⟨xs, h⟩,},
      { contradiction,}}},
end

example : (s \ t) ∪ (t \ s) = (s ∪ t) \ (s ∩ t) :=
begin
  ext x, split,
  { rintro (⟨xs, xnt⟩ | ⟨xt, xns⟩),
    { split,
      { left,
        from xs,},
      { rintro ⟨_, xt⟩,
        contradiction,},},
    { split,
      { right,
        from xt,},
      { rintro ⟨xs, xt⟩,
        contradiction,}}},
  { rintro ⟨xs | xt, xnst⟩,
    { dsimp at xnst,
      push_neg at xnst,
      left,
      from ⟨xs, xnst xs⟩,},
    { dsimp at xnst,
      push_neg at xnst,
      right,
      split,
      from xt,
      intro xs,
      from (xnst xs) xt,}},
end

def evens : set ℕ := {n | even n}
def odds : set ℕ := {n | ¬ even n}

example : evens ∪ odds = univ :=
begin
  rw [evens, odds],
  ext n,
  simp,
  apply classical.em,
end

example : { n | nat.prime n } ∩ { n | n > 2} ⊆ { n | ¬ even n } :=
begin
  intro n,
  dsimp,
  rintros ⟨pp, ngt⟩,
  apply nat.odd_iff_not_even.mp,
  cases nat.prime.eq_two_or_odd' pp with neq2 odn,
  linarith,
  from odn,
end

example (n : ℕ) (h : prime n) : nat.prime n :=
by rwa nat.prime_iff

end

section
variables (s t : set ℕ)

example (h₀ : ∀ x ∈ s, ¬ even x) (h₁ : ∀ x ∈ s, prime x) :
  ∀ x ∈ s, ¬ even x ∧ prime x :=
begin
  intros x xs,
  split,
  { apply h₀ x xs },
  apply h₁ x xs
end

example (h : ∃ x ∈ s, ¬ even x ∧ prime x) :
  ∃ x ∈ s, prime x :=
begin
  rcases h with ⟨x, xs, _, prime_x⟩,
  use [x, xs, prime_x]
end

section
variable (ssubt : s ⊆ t)

include ssubt

example (h₀ : ∀ x ∈ t, ¬ even x) (h₁ : ∀ x ∈ t, prime x) :
  ∀ x ∈ s, ¬ even x ∧ prime x :=
begin
  intros x xs,
  have : x ∈ t := ssubt xs,
  split,
    from h₀ x this,
    from h₁ x this,
end

example (h : ∃ x ∈ s, ¬ even x ∧ prime x) :
  ∃ x ∈ t, prime x :=
begin
  rcases h with ⟨x, xs, ne, px⟩,
  use [x, ssubt xs, px],
end

end

end

section
variables {α I : Type*}
variables A B : I → set α
variable  s : set α
open set

example : s ∩ (⋃ i, A i) = ⋃ i, (A i ∩ s) :=
begin
  ext x,
  simp only [mem_inter_eq, mem_Union],
  split,
  { rintro ⟨xs, ⟨i, xAi⟩⟩,
    use [i, xAi, xs],},
  { rintro ⟨i, ⟨xAi, xs⟩⟩,
    use [xs, i, xAi],},
end

example : (⋂ i, A i ∩ B i) = (⋂ i, A i) ∩ (⋂ i, B i) :=
begin
  ext x,
  simp only [mem_inter_eq, mem_Inter],
  split,
  { intro h,
    split,
    { intro i,
      exact (h i).1 },
    intro i,
    exact (h i).2 },
  rintros ⟨h1, h2⟩ i,
  split,
  { exact h1 i },
  exact h2 i
end

open_locale classical

example : s ∪ (⋂ i, A i) = ⋂ i, (A i ∪ s) :=
begin
  ext x,
  simp only [mem_union_eq, mem_Inter],
  split,
  { rintros (xs | xAi) i,
    right, from xs,
    left, from xAi i,},
  { rintros xsAi,
    by_cases h : x ∈ s,
    { left, from h,},
    { right, intro i,
      cases xsAi i with xAi xs,
      { from xAi,},
      { contradiction,},}},
end

def primes : set ℕ := {x | nat.prime x}

example : (⋃ p ∈ primes, {x | p^2 ∣ x}) = {x | ∃ p ∈ primes, p^2 ∣ x} :=
begin
  ext x,
  rw mem_Union₂,
  refl,
end

example : (⋃ p ∈ primes, {x | x ≤ p}) = univ :=
begin
  apply eq_univ_of_forall,
  intros x,
  rw mem_Union₂,
  dsimp,
  rcases nat.exists_infinite_primes x with ⟨p, pge, pp⟩,
  use [p, pp, pge],
end

end


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

/-!
# Erasing duplicates in a multiset.
-/

namespace multiset
open list

variables {α β : Type*} [decidable_eq α]

/-! ### erase_dup -/

/-- `erase_dup s` removes duplicates from `s`, yielding a `nodup` multiset. -/
def erase_dup (s : multiset α) : multiset α :=
quot.lift_on s (λ l, (l.erase_dup : multiset α))
  (λ s t p, quot.sound p.erase_dup)

@[simp] theorem coe_erase_dup (l : list α) : @erase_dup α _ l = l.erase_dup := rfl

@[simp] theorem erase_dup_zero : @erase_dup α _ 0 = 0 := rfl

@[simp] theorem mem_erase_dup {a : α} {s : multiset α} : a ∈ erase_dup s ↔ a ∈ s :=
quot.induction_on s $ λ l, mem_erase_dup

@[simp] theorem erase_dup_cons_of_mem {a : α} {s : multiset α} : a ∈ s →
  erase_dup (a ::ₘ s) = erase_dup s :=
quot.induction_on s $ λ l m, @congr_arg _ _ _ _ coe $ erase_dup_cons_of_mem m

@[simp] theorem erase_dup_cons_of_not_mem {a : α} {s : multiset α} : a ∉ s →
  erase_dup (a ::ₘ s) = a ::ₘ erase_dup s :=
quot.induction_on s $ λ l m, congr_arg coe $ erase_dup_cons_of_not_mem m

theorem erase_dup_le (s : multiset α) : erase_dup s ≤ s :=
quot.induction_on s $ λ l, (erase_dup_sublist _).subperm

theorem erase_dup_subset (s : multiset α) : erase_dup s ⊆ s :=
subset_of_le $ erase_dup_le _

theorem subset_erase_dup (s : multiset α) : s ⊆ erase_dup s :=
λ a, mem_erase_dup.2

@[simp] theorem erase_dup_subset' {s t : multiset α} : erase_dup s ⊆ t ↔ s ⊆ t :=
⟨subset.trans (subset_erase_dup _), subset.trans (erase_dup_subset _)⟩

@[simp] theorem subset_erase_dup' {s t : multiset α} : s ⊆ erase_dup t ↔ s ⊆ t :=
⟨λ h, subset.trans h (erase_dup_subset _), λ h, subset.trans h (subset_erase_dup _)⟩

@[simp] theorem nodup_erase_dup (s : multiset α) : nodup (erase_dup s) :=
quot.induction_on s nodup_erase_dup

theorem erase_dup_eq_self {s : multiset α} : erase_dup s = s ↔ nodup s :=
⟨λ e, e ▸ nodup_erase_dup s,
 quot.induction_on s $ λ l h, congr_arg coe $ erase_dup_eq_self.2 h⟩

theorem erase_dup_eq_zero {s : multiset α} : erase_dup s = 0 ↔ s = 0 :=
⟨λ h, eq_zero_of_subset_zero $ h ▸ subset_erase_dup _,
 λ h, h.symm ▸ erase_dup_zero⟩

@[simp] theorem erase_dup_singleton {a : α} : erase_dup (a ::ₘ 0) = a ::ₘ 0 :=
erase_dup_eq_self.2 $ nodup_singleton _

theorem le_erase_dup {s t : multiset α} : s ≤ erase_dup t ↔ s ≤ t ∧ nodup s :=
⟨λ h, ⟨le_trans h (erase_dup_le _), nodup_of_le h (nodup_erase_dup _)⟩,
 λ ⟨l, d⟩, (le_iff_subset d).2 $ subset.trans (subset_of_le l) (subset_erase_dup _)⟩

theorem erase_dup_ext {s t : multiset α} : erase_dup s = erase_dup t ↔ ∀ a, a ∈ s ↔ a ∈ t :=
by simp [nodup_ext]

theorem erase_dup_map_erase_dup_eq [decidable_eq β] (f : α → β) (s : multiset α) :
  erase_dup (map f (erase_dup s)) = erase_dup (map f s) := by simp [erase_dup_ext]

@[simp]
lemma erase_dup_nsmul {s : multiset α} {n : ℕ} (h0 : n ≠ 0) :
  (n • s).erase_dup = s.erase_dup :=
begin
  ext a,
  by_cases h : a ∈ s;
  simp [h,h0]
end

lemma nodup.le_erase_dup_iff_le {s t : multiset α} (hno : s.nodup) :
  s ≤ t.erase_dup ↔ s ≤ t :=
by simp [le_erase_dup, hno]

end multiset

lemma multiset.nodup.le_nsmul_iff_le {α : Type*} {s t : multiset α}
  {n : ℕ} (h : s.nodup) (hn : n ≠ 0) :
  s ≤ n • t ↔ s ≤ t :=
begin
  classical,
  rw [← h.le_erase_dup_iff_le, iff.comm, ← h.le_erase_dup_iff_le],
  simp [hn]
end