/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Neil Strickland

! This file was ported from Lean 3 source module data.pnat.basic
! leanprover-community/mathlib commit c3291da49cfa65f0d43b094750541c0731edc932
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Pnat.Defs
import Mathbin.Data.Nat.Bits
import Mathbin.Data.Nat.Order.Basic
import Mathbin.Data.Set.Basic
import Mathbin.Algebra.GroupWithZero.Divisibility
import Mathbin.Algebra.Order.Positive.Ring

/-!
# The positive natural numbers

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

This file develops the type `ℕ+` or `pnat`, the subtype of natural numbers that are positive.
It is defined in `data.pnat.defs`, but most of the development is deferred to here so
that `data.pnat.defs` can have very few imports.
-/

deriving instance AddLeftCancelSemigroup, AddRightCancelSemigroup, AddCommSemigroup,
  LinearOrderedCancelCommMonoid, Add, Mul, Distrib for PNat

namespace PNat

#print PNat.one_add_natPred /-
@[simp]
theorem one_add_natPred (n : ℕ+) : 1 + n.natPred = n := by
  rw [nat_pred, add_tsub_cancel_iff_le.mpr <| show 1 ≤ (n : ℕ) from n.2]
#align pnat.one_add_nat_pred PNat.one_add_natPred
-/

#print PNat.natPred_add_one /-
@[simp]
theorem natPred_add_one (n : ℕ+) : n.natPred + 1 = n :=
  (add_comm _ _).trans n.one_add_natPred
#align pnat.nat_pred_add_one PNat.natPred_add_one
-/

#print PNat.natPred_strictMono /-
@[mono]
theorem natPred_strictMono : StrictMono natPred := fun m n h => Nat.pred_lt_pred m.2.ne' h
#align pnat.nat_pred_strict_mono PNat.natPred_strictMono
-/

#print PNat.natPred_monotone /-
@[mono]
theorem natPred_monotone : Monotone natPred :=
  natPred_strictMono.Monotone
#align pnat.nat_pred_monotone PNat.natPred_monotone
-/

#print PNat.natPred_injective /-
theorem natPred_injective : Function.Injective natPred :=
  natPred_strictMono.Injective
#align pnat.nat_pred_injective PNat.natPred_injective
-/

#print PNat.natPred_lt_natPred /-
@[simp]
theorem natPred_lt_natPred {m n : ℕ+} : m.natPred < n.natPred ↔ m < n :=
  natPred_strictMono.lt_iff_lt
#align pnat.nat_pred_lt_nat_pred PNat.natPred_lt_natPred
-/

#print PNat.natPred_le_natPred /-
@[simp]
theorem natPred_le_natPred {m n : ℕ+} : m.natPred ≤ n.natPred ↔ m ≤ n :=
  natPred_strictMono.le_iff_le
#align pnat.nat_pred_le_nat_pred PNat.natPred_le_natPred
-/

#print PNat.natPred_inj /-
@[simp]
theorem natPred_inj {m n : ℕ+} : m.natPred = n.natPred ↔ m = n :=
  natPred_injective.eq_iff
#align pnat.nat_pred_inj PNat.natPred_inj
-/

end PNat

namespace Nat

#print Nat.succPNat_strictMono /-
@[mono]
theorem succPNat_strictMono : StrictMono succPNat := fun m n => Nat.succ_lt_succ
#align nat.succ_pnat_strict_mono Nat.succPNat_strictMono
-/

#print Nat.succPNat_mono /-
@[mono]
theorem succPNat_mono : Monotone succPNat :=
  succPNat_strictMono.Monotone
#align nat.succ_pnat_mono Nat.succPNat_mono
-/

#print Nat.succPNat_lt_succPNat /-
@[simp]
theorem succPNat_lt_succPNat {m n : ℕ} : m.succPNat < n.succPNat ↔ m < n :=
  succPNat_strictMono.lt_iff_lt
#align nat.succ_pnat_lt_succ_pnat Nat.succPNat_lt_succPNat
-/

#print Nat.succPNat_le_succPNat /-
@[simp]
theorem succPNat_le_succPNat {m n : ℕ} : m.succPNat ≤ n.succPNat ↔ m ≤ n :=
  succPNat_strictMono.le_iff_le
#align nat.succ_pnat_le_succ_pnat Nat.succPNat_le_succPNat
-/

#print Nat.succPNat_injective /-
theorem succPNat_injective : Function.Injective succPNat :=
  succPNat_strictMono.Injective
#align nat.succ_pnat_injective Nat.succPNat_injective
-/

#print Nat.succPNat_inj /-
@[simp]
theorem succPNat_inj {n m : ℕ} : succPNat n = succPNat m ↔ n = m :=
  succPNat_injective.eq_iff
#align nat.succ_pnat_inj Nat.succPNat_inj
-/

end Nat

namespace PNat

open Nat

#print PNat.coe_inj /-
/-- We now define a long list of structures on ℕ+ induced by
 similar structures on ℕ. Most of these behave in a completely
 obvious way, but there are a few things to be said about
 subtraction, division and powers.
-/
@[simp, norm_cast]
theorem coe_inj {m n : ℕ+} : (m : ℕ) = n ↔ m = n :=
  SetCoe.ext_iff
#align pnat.coe_inj PNat.coe_inj
-/

/- warning: pnat.add_coe -> PNat.add_coe is a dubious translation:
lean 3 declaration is
  forall (m : PNat) (n : PNat), Eq.{1} Nat ((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))) (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) m n)) (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))) m) ((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))) n))
but is expected to have type
  forall (m : PNat) (n : PNat), Eq.{1} Nat (PNat.val (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) m n)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (PNat.val m) (PNat.val n))
Case conversion may be inaccurate. Consider using '#align pnat.add_coe PNat.add_coeₓ'. -/
@[simp, norm_cast]
theorem add_coe (m n : ℕ+) : ((m + n : ℕ+) : ℕ) = m + n :=
  rfl
#align pnat.add_coe PNat.add_coe

/- warning: pnat.coe_add_hom -> PNat.coeAddHom is a dubious translation:
lean 3 declaration is
  AddHom.{0, 0} PNat Nat PNat.hasAdd Nat.hasAdd
but is expected to have type
  AddHom.{0, 0} PNat Nat instPNatAdd instAddNat
Case conversion may be inaccurate. Consider using '#align pnat.coe_add_hom PNat.coeAddHomₓ'. -/
/-- `pnat.coe` promoted to an `add_hom`, that is, a morphism which preserves addition. -/
def coeAddHom : AddHom ℕ+ ℕ where
  toFun := coe
  map_add' := add_coe
#align pnat.coe_add_hom PNat.coeAddHom

instance : CovariantClass ℕ+ ℕ+ (· + ·) (· ≤ ·) :=
  Positive.covariantClass_add_le

instance : CovariantClass ℕ+ ℕ+ (· + ·) (· < ·) :=
  Positive.covariantClass_add_lt

instance : ContravariantClass ℕ+ ℕ+ (· + ·) (· ≤ ·) :=
  Positive.contravariantClass_add_le

instance : ContravariantClass ℕ+ ℕ+ (· + ·) (· < ·) :=
  Positive.contravariantClass_add_lt

#print Equiv.pnatEquivNat /-
/-- An equivalence between `ℕ+` and `ℕ` given by `pnat.nat_pred` and `nat.succ_pnat`. -/
@[simps (config := { fullyApplied := false })]
def Equiv.pnatEquivNat : ℕ+ ≃ ℕ where
  toFun := PNat.natPred
  invFun := Nat.succPNat
  left_inv := succPNat_natPred
  right_inv := Nat.natPred_succPNat
#align equiv.pnat_equiv_nat Equiv.pnatEquivNat
-/

#print OrderIso.pnatIsoNat /-
/-- The order isomorphism between ℕ and ℕ+ given by `succ`. -/
@[simps (config := { fullyApplied := false }) apply]
def OrderIso.pnatIsoNat : ℕ+ ≃o ℕ
    where
  toEquiv := Equiv.pnatEquivNat
  map_rel_iff' _ _ := natPred_le_natPred
#align order_iso.pnat_iso_nat OrderIso.pnatIsoNat
-/

/- warning: order_iso.pnat_iso_nat_symm_apply -> OrderIso.pnatIsoNat_symm_apply is a dubious translation:
lean 3 declaration is
  Eq.{1} (Nat -> PNat) (coeFn.{1, 1} (OrderIso.{0, 0} Nat PNat Nat.hasLe (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))) (fun (_x : RelIso.{0, 0} Nat PNat (LE.le.{0} Nat Nat.hasLe) (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))))) => Nat -> PNat) (RelIso.hasCoeToFun.{0, 0} Nat PNat (LE.le.{0} Nat Nat.hasLe) (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))))) (OrderIso.symm.{0, 0} PNat Nat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) Nat.hasLe OrderIso.pnatIsoNat)) Nat.succPNat
but is expected to have type
  Eq.{1} (forall (ᾰ : Nat), (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Nat) => PNat) ᾰ) (FunLike.coe.{1, 1, 1} (Function.Embedding.{1, 1} Nat PNat) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Nat) => PNat) _x) (EmbeddingLike.toFunLike.{1, 1, 1} (Function.Embedding.{1, 1} Nat PNat) Nat PNat (Function.instEmbeddingLikeEmbedding.{1, 1} Nat PNat)) (RelEmbedding.toEmbedding.{0, 0} Nat PNat (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : Nat) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : Nat) => LE.le.{0} Nat instLENat x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : PNat) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : PNat) => LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{0, 0} Nat PNat (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : Nat) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : Nat) => LE.le.{0} Nat instLENat x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : PNat) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : PNat) => LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (OrderIso.symm.{0, 0} PNat Nat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) instLENat OrderIso.pnatIsoNat)))) Nat.succPNat
Case conversion may be inaccurate. Consider using '#align order_iso.pnat_iso_nat_symm_apply OrderIso.pnatIsoNat_symm_applyₓ'. -/
@[simp]
theorem OrderIso.pnatIsoNat_symm_apply : ⇑OrderIso.pnatIsoNat.symm = Nat.succPNat :=
  rfl
#align order_iso.pnat_iso_nat_symm_apply OrderIso.pnatIsoNat_symm_apply

/- warning: pnat.lt_add_one_iff -> PNat.lt_add_one_iff is a dubious translation:
lean 3 declaration is
  forall {a : PNat} {b : PNat}, Iff (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) a (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) b (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne))))) (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) a b)
but is expected to have type
  forall {a : PNat} {b : PNat}, Iff (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) a (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) b (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))))) (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) a b)
Case conversion may be inaccurate. Consider using '#align pnat.lt_add_one_iff PNat.lt_add_one_iffₓ'. -/
theorem lt_add_one_iff : ∀ {a b : ℕ+}, a < b + 1 ↔ a ≤ b := fun a b => Nat.lt_add_one_iff
#align pnat.lt_add_one_iff PNat.lt_add_one_iff

/- warning: pnat.add_one_le_iff -> PNat.add_one_le_iff is a dubious translation:
lean 3 declaration is
  forall {a : PNat} {b : PNat}, Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) a (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))) b) (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) a b)
but is expected to have type
  forall {a : PNat} {b : PNat}, Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) a (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) b) (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) a b)
Case conversion may be inaccurate. Consider using '#align pnat.add_one_le_iff PNat.add_one_le_iffₓ'. -/
theorem add_one_le_iff : ∀ {a b : ℕ+}, a + 1 ≤ b ↔ a < b := fun a b => Nat.add_one_le_iff
#align pnat.add_one_le_iff PNat.add_one_le_iff

instance : OrderBot ℕ+ where
  bot := 1
  bot_le a := a.property

/- warning: pnat.bot_eq_one -> PNat.bot_eq_one is a dubious translation:
lean 3 declaration is
  Eq.{1} PNat (Bot.bot.{0} PNat (OrderBot.toHasBot.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) PNat.orderBot)) (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))
but is expected to have type
  Eq.{1} PNat (Bot.bot.{0} PNat (OrderBot.toBot.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) PNat.instOrderBotPNatToLEToPreorderToPartialOrderToOrderedCancelCommMonoidInstPNatLinearOrderedCancelCommMonoid)) (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))
Case conversion may be inaccurate. Consider using '#align pnat.bot_eq_one PNat.bot_eq_oneₓ'. -/
@[simp]
theorem bot_eq_one : (⊥ : ℕ+) = 1 :=
  rfl
#align pnat.bot_eq_one PNat.bot_eq_one

/- warning: pnat.mk_bit0 -> PNat.mk_bit0 is a dubious translation:
lean 3 declaration is
  forall (n : Nat) {h : LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) (bit0.{0} Nat Nat.hasAdd n)}, Eq.{1} (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)) (Subtype.mk.{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) (bit0.{0} Nat Nat.hasAdd n) h) (bit0.{0} PNat PNat.hasAdd (Subtype.mk.{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) n (Nat.pos_of_bit0_pos n h)))
but is expected to have type
  forall (n : Nat) {h : LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) (bit0.{0} Nat instAddNat n)}, Eq.{1} (Subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) (Subtype.mk.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) (bit0.{0} Nat instAddNat n) h) (bit0.{0} PNat instPNatAdd (Subtype.mk.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) n (Nat.pos_of_bit0_pos n h)))
Case conversion may be inaccurate. Consider using '#align pnat.mk_bit0 PNat.mk_bit0ₓ'. -/
-- Some lemmas that rewrite `pnat.mk n h`, for `n` an explicit numeral, into explicit numerals.
@[simp]
theorem mk_bit0 (n) {h} : (⟨bit0 n, h⟩ : ℕ+) = (bit0 ⟨n, pos_of_bit0_pos h⟩ : ℕ+) :=
  rfl
#align pnat.mk_bit0 PNat.mk_bit0

/- warning: pnat.mk_bit1 -> PNat.mk_bit1 is a dubious translation:
lean 3 declaration is
  forall (n : Nat) {h : LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) (bit1.{0} Nat Nat.hasOne Nat.hasAdd n)} {k : LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n}, Eq.{1} (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)) (Subtype.mk.{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) (bit1.{0} Nat Nat.hasOne Nat.hasAdd n) h) (bit1.{0} PNat PNat.hasOne PNat.hasAdd (Subtype.mk.{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) n k))
but is expected to have type
  forall (n : Nat) {h : LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) (bit1.{0} Nat (CanonicallyOrderedCommSemiring.toOne.{0} Nat Nat.canonicallyOrderedCommSemiring) instAddNat n)} {k : LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n}, Eq.{1} (Subtype.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)) (Subtype.mk.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) (bit1.{0} Nat (CanonicallyOrderedCommSemiring.toOne.{0} Nat Nat.canonicallyOrderedCommSemiring) instAddNat n) h) (bit1.{0} PNat instOnePNat instPNatAdd (Subtype.mk.{1} Nat (fun (n : Nat) => LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) n k))
Case conversion may be inaccurate. Consider using '#align pnat.mk_bit1 PNat.mk_bit1ₓ'. -/
@[simp]
theorem mk_bit1 (n) {h} {k} : (⟨bit1 n, h⟩ : ℕ+) = (bit1 ⟨n, k⟩ : ℕ+) :=
  rfl
#align pnat.mk_bit1 PNat.mk_bit1

/- warning: pnat.bit0_le_bit0 -> PNat.bit0_le_bit0 is a dubious translation:
lean 3 declaration is
  forall (n : PNat) (m : PNat), Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) (bit0.{0} PNat PNat.hasAdd n) (bit0.{0} PNat PNat.hasAdd m)) (LE.le.{0} Nat Nat.hasLe (bit0.{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))) n)) (bit0.{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))) m)))
but is expected to have type
  forall (n : PNat) (m : PNat), Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) (bit0.{0} PNat instPNatAdd n) (bit0.{0} PNat instPNatAdd m)) (LE.le.{0} Nat instLENat (bit0.{0} Nat instAddNat (PNat.val n)) (bit0.{0} Nat instAddNat (PNat.val m)))
Case conversion may be inaccurate. Consider using '#align pnat.bit0_le_bit0 PNat.bit0_le_bit0ₓ'. -/
-- Some lemmas that rewrite inequalities between explicit numerals in `ℕ+`
-- into the corresponding inequalities in `ℕ`.
-- TODO: perhaps this should not be attempted by `simp`,
-- and instead we should expect `norm_num` to take care of these directly?
-- TODO: these lemmas are perhaps incomplete:
-- * 1 is not represented as a bit0 or bit1
-- * strict inequalities?
@[simp]
theorem bit0_le_bit0 (n m : ℕ+) : bit0 n ≤ bit0 m ↔ bit0 (n : ℕ) ≤ bit0 (m : ℕ) :=
  Iff.rfl
#align pnat.bit0_le_bit0 PNat.bit0_le_bit0

/- warning: pnat.bit0_le_bit1 -> PNat.bit0_le_bit1 is a dubious translation:
lean 3 declaration is
  forall (n : PNat) (m : PNat), Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) (bit0.{0} PNat PNat.hasAdd n) (bit1.{0} PNat PNat.hasOne PNat.hasAdd m)) (LE.le.{0} Nat Nat.hasLe (bit0.{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))) n)) (bit1.{0} Nat Nat.hasOne 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))) m)))
but is expected to have type
  forall (n : PNat) (m : PNat), Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) (bit0.{0} PNat instPNatAdd n) (bit1.{0} PNat instOnePNat instPNatAdd m)) (LE.le.{0} Nat instLENat (bit0.{0} Nat instAddNat (PNat.val n)) (bit1.{0} Nat (CanonicallyOrderedCommSemiring.toOne.{0} Nat Nat.canonicallyOrderedCommSemiring) instAddNat (PNat.val m)))
Case conversion may be inaccurate. Consider using '#align pnat.bit0_le_bit1 PNat.bit0_le_bit1ₓ'. -/
@[simp]
theorem bit0_le_bit1 (n m : ℕ+) : bit0 n ≤ bit1 m ↔ bit0 (n : ℕ) ≤ bit1 (m : ℕ) :=
  Iff.rfl
#align pnat.bit0_le_bit1 PNat.bit0_le_bit1

/- warning: pnat.bit1_le_bit0 -> PNat.bit1_le_bit0 is a dubious translation:
lean 3 declaration is
  forall (n : PNat) (m : PNat), Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) (bit1.{0} PNat PNat.hasOne PNat.hasAdd n) (bit0.{0} PNat PNat.hasAdd m)) (LE.le.{0} Nat Nat.hasLe (bit1.{0} Nat Nat.hasOne 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))) n)) (bit0.{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))) m)))
but is expected to have type
  forall (n : PNat) (m : PNat), Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) (bit1.{0} PNat instOnePNat instPNatAdd n) (bit0.{0} PNat instPNatAdd m)) (LE.le.{0} Nat instLENat (bit1.{0} Nat (CanonicallyOrderedCommSemiring.toOne.{0} Nat Nat.canonicallyOrderedCommSemiring) instAddNat (PNat.val n)) (bit0.{0} Nat instAddNat (PNat.val m)))
Case conversion may be inaccurate. Consider using '#align pnat.bit1_le_bit0 PNat.bit1_le_bit0ₓ'. -/
@[simp]
theorem bit1_le_bit0 (n m : ℕ+) : bit1 n ≤ bit0 m ↔ bit1 (n : ℕ) ≤ bit0 (m : ℕ) :=
  Iff.rfl
#align pnat.bit1_le_bit0 PNat.bit1_le_bit0

/- warning: pnat.bit1_le_bit1 -> PNat.bit1_le_bit1 is a dubious translation:
lean 3 declaration is
  forall (n : PNat) (m : PNat), Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) (bit1.{0} PNat PNat.hasOne PNat.hasAdd n) (bit1.{0} PNat PNat.hasOne PNat.hasAdd m)) (LE.le.{0} Nat Nat.hasLe (bit1.{0} Nat Nat.hasOne 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))) n)) (bit1.{0} Nat Nat.hasOne 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))) m)))
but is expected to have type
  forall (n : PNat) (m : PNat), Iff (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) (bit1.{0} PNat instOnePNat instPNatAdd n) (bit1.{0} PNat instOnePNat instPNatAdd m)) (LE.le.{0} Nat instLENat (bit1.{0} Nat (CanonicallyOrderedCommSemiring.toOne.{0} Nat Nat.canonicallyOrderedCommSemiring) instAddNat (PNat.val n)) (bit1.{0} Nat (CanonicallyOrderedCommSemiring.toOne.{0} Nat Nat.canonicallyOrderedCommSemiring) instAddNat (PNat.val m)))
Case conversion may be inaccurate. Consider using '#align pnat.bit1_le_bit1 PNat.bit1_le_bit1ₓ'. -/
@[simp]
theorem bit1_le_bit1 (n m : ℕ+) : bit1 n ≤ bit1 m ↔ bit1 (n : ℕ) ≤ bit1 (m : ℕ) :=
  Iff.rfl
#align pnat.bit1_le_bit1 PNat.bit1_le_bit1

/- warning: pnat.mul_coe -> PNat.mul_coe is a dubious translation:
lean 3 declaration is
  forall (m : PNat) (n : PNat), Eq.{1} Nat ((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))) (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) m n)) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) ((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))) m) ((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))) n))
but is expected to have type
  forall (m : PNat) (n : PNat), Eq.{1} Nat (PNat.val (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) m n)) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) (PNat.val m) (PNat.val n))
Case conversion may be inaccurate. Consider using '#align pnat.mul_coe PNat.mul_coeₓ'. -/
@[simp, norm_cast]
theorem mul_coe (m n : ℕ+) : ((m * n : ℕ+) : ℕ) = m * n :=
  rfl
#align pnat.mul_coe PNat.mul_coe

#print PNat.coeMonoidHom /-
/-- `pnat.coe` promoted to a `monoid_hom`. -/
def coeMonoidHom : ℕ+ →* ℕ where
  toFun := coe
  map_one' := one_coe
  map_mul' := mul_coe
#align pnat.coe_monoid_hom PNat.coeMonoidHom
-/

/- warning: pnat.coe_coe_monoid_hom -> PNat.coe_coeMonoidHom is a dubious translation:
lean 3 declaration is
  Eq.{1} ((fun (_x : MonoidHom.{0, 0} PNat Nat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))))) (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) => PNat -> Nat) PNat.coeMonoidHom) (coeFn.{1, 1} (MonoidHom.{0, 0} PNat Nat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))))) (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) (fun (_x : MonoidHom.{0, 0} PNat Nat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))))) (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) => PNat -> Nat) (MonoidHom.hasCoeToFun.{0, 0} PNat Nat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))))) (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) PNat.coeMonoidHom) ((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))))
but is expected to have type
  Eq.{1} (forall (a : PNat), (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : PNat) => Nat) a) (FunLike.coe.{1, 1, 1} (MonoidHom.{0, 0} PNat Nat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))))) (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) PNat (fun (_x : PNat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : PNat) => Nat) _x) (MulHomClass.toFunLike.{0, 0, 0} (MonoidHom.{0, 0} PNat Nat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))))) (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) PNat Nat (MulOneClass.toMul.{0} PNat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) (MulOneClass.toMul.{0} Nat (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) (MonoidHomClass.toMulHomClass.{0, 0, 0} (MonoidHom.{0, 0} PNat Nat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))))) (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) PNat Nat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))))) (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring))) (MonoidHom.monoidHomClass.{0, 0} PNat Nat (Monoid.toMulOneClass.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))))) (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))))) PNat.coeMonoidHom) (Coe.coe.{1, 1} PNat Nat coePNatNat)
Case conversion may be inaccurate. Consider using '#align pnat.coe_coe_monoid_hom PNat.coe_coeMonoidHomₓ'. -/
@[simp]
theorem coe_coeMonoidHom : (coeMonoidHom : ℕ+ → ℕ) = coe :=
  rfl
#align pnat.coe_coe_monoid_hom PNat.coe_coeMonoidHom

#print PNat.le_one_iff /-
@[simp]
theorem le_one_iff {n : ℕ+} : n ≤ 1 ↔ n = 1 :=
  le_bot_iff
#align pnat.le_one_iff PNat.le_one_iff
-/

/- warning: pnat.lt_add_left -> PNat.lt_add_left is a dubious translation:
lean 3 declaration is
  forall (n : PNat) (m : PNat), LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) n (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) m n)
but is expected to have type
  forall (n : PNat) (m : PNat), LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) n (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) m n)
Case conversion may be inaccurate. Consider using '#align pnat.lt_add_left PNat.lt_add_leftₓ'. -/
theorem lt_add_left (n m : ℕ+) : n < m + n :=
  lt_add_of_pos_left _ m.2
#align pnat.lt_add_left PNat.lt_add_left

/- warning: pnat.lt_add_right -> PNat.lt_add_right is a dubious translation:
lean 3 declaration is
  forall (n : PNat) (m : PNat), LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) n (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) n m)
but is expected to have type
  forall (n : PNat) (m : PNat), LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) n (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) n m)
Case conversion may be inaccurate. Consider using '#align pnat.lt_add_right PNat.lt_add_rightₓ'. -/
theorem lt_add_right (n m : ℕ+) : n < n + m :=
  (lt_add_left n m).trans_eq (add_comm _ _)
#align pnat.lt_add_right PNat.lt_add_right

/- warning: pnat.coe_bit0 -> PNat.coe_bit0 is a dubious translation:
lean 3 declaration is
  forall (a : PNat), Eq.{1} Nat ((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))) (bit0.{0} PNat PNat.hasAdd a)) (bit0.{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))) a))
but is expected to have type
  forall (a : PNat), Eq.{1} Nat (PNat.val (bit0.{0} PNat instPNatAdd a)) (bit0.{0} Nat instAddNat (PNat.val a))
Case conversion may be inaccurate. Consider using '#align pnat.coe_bit0 PNat.coe_bit0ₓ'. -/
@[simp, norm_cast]
theorem coe_bit0 (a : ℕ+) : ((bit0 a : ℕ+) : ℕ) = bit0 (a : ℕ) :=
  rfl
#align pnat.coe_bit0 PNat.coe_bit0

/- warning: pnat.coe_bit1 -> PNat.coe_bit1 is a dubious translation:
lean 3 declaration is
  forall (a : PNat), Eq.{1} Nat ((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))) (bit1.{0} PNat PNat.hasOne PNat.hasAdd a)) (bit1.{0} Nat Nat.hasOne 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))) a))
but is expected to have type
  forall (a : PNat), Eq.{1} Nat (PNat.val (bit1.{0} PNat instOnePNat instPNatAdd a)) (bit1.{0} Nat (CanonicallyOrderedCommSemiring.toOne.{0} Nat Nat.canonicallyOrderedCommSemiring) instAddNat (PNat.val a))
Case conversion may be inaccurate. Consider using '#align pnat.coe_bit1 PNat.coe_bit1ₓ'. -/
@[simp, norm_cast]
theorem coe_bit1 (a : ℕ+) : ((bit1 a : ℕ+) : ℕ) = bit1 (a : ℕ) :=
  rfl
#align pnat.coe_bit1 PNat.coe_bit1

#print PNat.pow_coe /-
@[simp, norm_cast]
theorem pow_coe (m : ℕ+) (n : ℕ) : ((m ^ n : ℕ+) : ℕ) = (m : ℕ) ^ n :=
  rfl
#align pnat.pow_coe PNat.pow_coe
-/

/-- Subtraction a - b is defined in the obvious way when
  a > b, and by a - b = 1 if a ≤ b.
-/
instance : Sub ℕ+ :=
  ⟨fun a b => toPNat' (a - b : ℕ)⟩

/- warning: pnat.sub_coe -> PNat.sub_coe is a dubious translation:
lean 3 declaration is
  forall (a : PNat) (b : PNat), Eq.{1} Nat ((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))) (HSub.hSub.{0, 0, 0} PNat PNat PNat (instHSub.{0} PNat PNat.hasSub) a b)) (ite.{1} Nat (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) b a) (Subtype.decidableLT.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (fun (a : Nat) (b : Nat) => Nat.decidableLt a b) (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) b a) (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))) 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)) (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 (PNat.val (HSub.hSub.{0, 0, 0} PNat PNat PNat (instHSub.{0} PNat PNat.instSubPNat) a b)) (ite.{1} Nat (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) b a) (instDecidableLtToLTToPreorderToPartialOrder.{0} PNat instPNatLinearOrder b a) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (PNat.val a) (PNat.val b)) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align pnat.sub_coe PNat.sub_coeₓ'. -/
theorem sub_coe (a b : ℕ+) : ((a - b : ℕ+) : ℕ) = ite (b < a) (a - b : ℕ) 1 :=
  by
  change (to_pnat' _ : ℕ) = ite _ _ _
  split_ifs with h
  · exact to_pnat'_coe (tsub_pos_of_lt h)
  · rw [tsub_eq_zero_iff_le.mpr (le_of_not_gt h : (a : ℕ) ≤ b)]
    rfl
#align pnat.sub_coe PNat.sub_coe

/- warning: pnat.add_sub_of_lt -> PNat.add_sub_of_lt is a dubious translation:
lean 3 declaration is
  forall {a : PNat} {b : PNat}, (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) a b) -> (Eq.{1} PNat (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) a (HSub.hSub.{0, 0, 0} PNat PNat PNat (instHSub.{0} PNat PNat.hasSub) b a)) b)
but is expected to have type
  forall {a : PNat} {b : PNat}, (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) a b) -> (Eq.{1} PNat (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) a (HSub.hSub.{0, 0, 0} PNat PNat PNat (instHSub.{0} PNat PNat.instSubPNat) b a)) b)
Case conversion may be inaccurate. Consider using '#align pnat.add_sub_of_lt PNat.add_sub_of_ltₓ'. -/
theorem add_sub_of_lt {a b : ℕ+} : a < b → a + (b - a) = b := fun h =>
  eq <| by
    rw [add_coe, sub_coe, if_pos h]
    exact add_tsub_cancel_of_le h.le
#align pnat.add_sub_of_lt PNat.add_sub_of_lt

/- warning: pnat.exists_eq_succ_of_ne_one -> PNat.exists_eq_succ_of_ne_one is a dubious translation:
lean 3 declaration is
  forall {n : PNat}, (Ne.{1} PNat n (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))) -> (Exists.{1} PNat (fun (k : PNat) => Eq.{1} PNat n (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) k (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne))))))
but is expected to have type
  forall {n : PNat}, (Ne.{1} PNat n (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) -> (Exists.{1} PNat (fun (k : PNat) => Eq.{1} PNat n (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) k (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))))))
Case conversion may be inaccurate. Consider using '#align pnat.exists_eq_succ_of_ne_one PNat.exists_eq_succ_of_ne_oneₓ'. -/
/-- If `n : ℕ+` is different from `1`, then it is the successor of some `k : ℕ+`. -/
theorem exists_eq_succ_of_ne_one : ∀ {n : ℕ+} (h1 : n ≠ 1), ∃ k : ℕ+, n = k + 1
  | ⟨1, _⟩, h1 => False.elim <| h1 rfl
  | ⟨n + 2, _⟩, _ => ⟨⟨n + 1, by simp⟩, rfl⟩
#align pnat.exists_eq_succ_of_ne_one PNat.exists_eq_succ_of_ne_one

/- warning: pnat.case_strong_induction_on -> PNat.caseStrongInductionOn is a dubious translation:
lean 3 declaration is
  forall {p : PNat -> Sort.{u1}} (a : PNat), (p (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))) -> (forall (n : PNat), (forall (m : PNat), (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) m n) -> (p m)) -> (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) n (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))))) -> (p a)
but is expected to have type
  forall {p : PNat -> Sort.{u1}} (a : PNat), (p (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) -> (forall (n : PNat), (forall (m : PNat), (LE.le.{0} PNat (Preorder.toLE.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) m n) -> (p m)) -> (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) n (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))))) -> (p a)
Case conversion may be inaccurate. Consider using '#align pnat.case_strong_induction_on PNat.caseStrongInductionOnₓ'. -/
/-- Strong induction on `ℕ+`, with `n = 1` treated separately. -/
def caseStrongInductionOn {p : ℕ+ → Sort _} (a : ℕ+) (hz : p 1)
    (hi : ∀ n, (∀ m, m ≤ n → p m) → p (n + 1)) : p a :=
  by
  apply strong_induction_on a
  rintro ⟨k, kprop⟩ hk
  cases' k with k
  · exact (lt_irrefl 0 kprop).elim
  cases' k with k
  · exact hz
  exact hi ⟨k.succ, Nat.succ_pos _⟩ fun m hm => hk _ (lt_succ_iff.2 hm)
#align pnat.case_strong_induction_on PNat.caseStrongInductionOn

/- warning: pnat.rec_on -> PNat.recOn is a dubious translation:
lean 3 declaration is
  forall (n : PNat) {p : PNat -> Sort.{u1}}, (p (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))) -> (forall (n : PNat), (p n) -> (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) n (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))))) -> (p n)
but is expected to have type
  forall (n : PNat) {p : PNat -> Sort.{u1}}, (p (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) -> (forall (n : PNat), (p n) -> (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) n (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))))) -> (p n)
Case conversion may be inaccurate. Consider using '#align pnat.rec_on PNat.recOnₓ'. -/
/-- An induction principle for `ℕ+`: it takes values in `Sort*`, so it applies also to Types,
not only to `Prop`. -/
@[elab_as_elim]
def recOn (n : ℕ+) {p : ℕ+ → Sort _} (p1 : p 1) (hp : ∀ n, p n → p (n + 1)) : p n :=
  by
  rcases n with ⟨n, h⟩
  induction' n with n IH
  · exact absurd h (by decide)
  · cases' n with n
    · exact p1
    · exact hp _ (IH n.succ_pos)
#align pnat.rec_on PNat.recOn

/- warning: pnat.rec_on_one -> PNat.recOn_one is a dubious translation:
lean 3 declaration is
  forall {p : PNat -> Sort.{u1}} (p1 : p (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))) (hp : forall (n : PNat), (p n) -> (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) n (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))))), Eq.{u1} (p (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))) (PNat.recOn.{u1} (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne))) p p1 hp) p1
but is expected to have type
  forall {p : PNat -> Sort.{u1}} (p1 : p (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) (hp : forall (n : PNat), (p n) -> (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) n (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))))), Eq.{u1} (p (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) (PNat.recOn.{u1} (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))) p p1 hp) p1
Case conversion may be inaccurate. Consider using '#align pnat.rec_on_one PNat.recOn_oneₓ'. -/
@[simp]
theorem recOn_one {p} (p1 hp) : @PNat.recOn 1 p p1 hp = p1 :=
  rfl
#align pnat.rec_on_one PNat.recOn_one

/- warning: pnat.rec_on_succ -> PNat.recOn_succ is a dubious translation:
lean 3 declaration is
  forall (n : PNat) {p : PNat -> Sort.{u1}} (p1 : p (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))) (hp : forall (n : PNat), (p n) -> (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) n (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))))), Eq.{u1} (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) n (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne))))) (PNat.recOn.{u1} (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat PNat.hasAdd) n (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))) p p1 hp) (hp n (PNat.recOn.{u1} n p p1 hp))
but is expected to have type
  forall (n : PNat) {p : PNat -> Sort.{u1}} (p1 : p (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) (hp : forall (n : PNat), (p n) -> (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) n (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))))), Eq.{u1} (p (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) n (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))))) (PNat.recOn.{u1} (HAdd.hAdd.{0, 0, 0} PNat PNat PNat (instHAdd.{0} PNat instPNatAdd) n (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) p p1 hp) (hp n (PNat.recOn.{u1} n p p1 hp))
Case conversion may be inaccurate. Consider using '#align pnat.rec_on_succ PNat.recOn_succₓ'. -/
@[simp]
theorem recOn_succ (n : ℕ+) {p : ℕ+ → Sort _} (p1 hp) :
    @PNat.recOn (n + 1) p p1 hp = hp n (@PNat.recOn n p p1 hp) :=
  by
  cases' n with n h
  cases n <;> [exact absurd h (by decide), rfl]
#align pnat.rec_on_succ PNat.recOn_succ

#print PNat.modDivAux_spec /-
theorem modDivAux_spec :
    ∀ (k : ℕ+) (r q : ℕ) (h : ¬(r = 0 ∧ q = 0)),
      ((modDivAux k r q).1 : ℕ) + k * (modDivAux k r q).2 = r + k * q
  | k, 0, 0, h => (h ⟨rfl, rfl⟩).elim
  | k, 0, q + 1, h =>
    by
    change (k : ℕ) + (k : ℕ) * (q + 1).pred = 0 + (k : ℕ) * (q + 1)
    rw [Nat.pred_succ, Nat.mul_succ, zero_add, add_comm]
  | k, r + 1, q, h => rfl
#align pnat.mod_div_aux_spec PNat.modDivAux_spec
-/

#print PNat.mod_add_div /-
theorem mod_add_div (m k : ℕ+) : (mod m k + k * div m k : ℕ) = m :=
  by
  let h₀ := Nat.mod_add_div (m : ℕ) (k : ℕ)
  have : ¬((m : ℕ) % (k : ℕ) = 0 ∧ (m : ℕ) / (k : ℕ) = 0) :=
    by
    rintro ⟨hr, hq⟩
    rw [hr, hq, MulZeroClass.mul_zero, zero_add] at h₀
    exact (m.ne_zero h₀.symm).elim
  have := mod_div_aux_spec k ((m : ℕ) % (k : ℕ)) ((m : ℕ) / (k : ℕ)) this
  exact this.trans h₀
#align pnat.mod_add_div PNat.mod_add_div
-/

#print PNat.div_add_mod /-
theorem div_add_mod (m k : ℕ+) : (k * div m k + mod m k : ℕ) = m :=
  (add_comm _ _).trans (mod_add_div _ _)
#align pnat.div_add_mod PNat.div_add_mod
-/

#print PNat.mod_add_div' /-
theorem mod_add_div' (m k : ℕ+) : (mod m k + div m k * k : ℕ) = m :=
  by
  rw [mul_comm]
  exact mod_add_div _ _
#align pnat.mod_add_div' PNat.mod_add_div'
-/

#print PNat.div_add_mod' /-
theorem div_add_mod' (m k : ℕ+) : (div m k * k + mod m k : ℕ) = m :=
  by
  rw [mul_comm]
  exact div_add_mod _ _
#align pnat.div_add_mod' PNat.div_add_mod'
-/

#print PNat.mod_le /-
theorem mod_le (m k : ℕ+) : mod m k ≤ m ∧ mod m k ≤ k :=
  by
  change (mod m k : ℕ) ≤ (m : ℕ) ∧ (mod m k : ℕ) ≤ (k : ℕ)
  rw [mod_coe]; split_ifs
  · have hm : (m : ℕ) > 0 := m.pos
    rw [← Nat.mod_add_div (m : ℕ) (k : ℕ), h, zero_add] at hm⊢
    by_cases h' : (m : ℕ) / (k : ℕ) = 0
    · rw [h', MulZeroClass.mul_zero] at hm
      exact (lt_irrefl _ hm).elim
    · let h' := Nat.mul_le_mul_left (k : ℕ) (Nat.succ_le_of_lt (Nat.pos_of_ne_zero h'))
      rw [mul_one] at h'
      exact ⟨h', le_refl (k : ℕ)⟩
  · exact ⟨Nat.mod_le (m : ℕ) (k : ℕ), (Nat.mod_lt (m : ℕ) k.pos).le⟩
#align pnat.mod_le PNat.mod_le
-/

#print PNat.dvd_iff /-
theorem dvd_iff {k m : ℕ+} : k ∣ m ↔ (k : ℕ) ∣ (m : ℕ) :=
  by
  constructor <;> intro h; rcases h with ⟨_, rfl⟩; apply dvd_mul_right
  rcases h with ⟨a, h⟩; cases a;
  · contrapose h
    apply NeZero
  use a.succ; apply Nat.succ_pos; rw [← coe_inj, h, mul_coe, mk_coe]
#align pnat.dvd_iff PNat.dvd_iff
-/

#print PNat.dvd_iff' /-
theorem dvd_iff' {k m : ℕ+} : k ∣ m ↔ mod m k = k :=
  by
  rw [dvd_iff]
  rw [Nat.dvd_iff_mod_eq_zero]; constructor
  · intro h
    apply Eq
    rw [mod_coe, if_pos h]
  · intro h
    by_cases h' : (m : ℕ) % (k : ℕ) = 0
    · exact h'
    · replace h : (mod m k : ℕ) = (k : ℕ) := congr_arg _ h
      rw [mod_coe, if_neg h'] at h
      exact ((Nat.mod_lt (m : ℕ) k.pos).Ne h).elim
#align pnat.dvd_iff' PNat.dvd_iff'
-/

#print PNat.le_of_dvd /-
theorem le_of_dvd {m n : ℕ+} : m ∣ n → m ≤ n :=
  by
  rw [dvd_iff']
  intro h
  rw [← h]
  apply (mod_le n m).left
#align pnat.le_of_dvd PNat.le_of_dvd
-/

/- warning: pnat.mul_div_exact -> PNat.mul_div_exact is a dubious translation:
lean 3 declaration is
  forall {m : PNat} {k : PNat}, (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) k m) -> (Eq.{1} PNat (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) k (PNat.divExact m k)) m)
but is expected to have type
  forall {m : PNat} {k : PNat}, (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) k m) -> (Eq.{1} PNat (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) k (PNat.divExact m k)) m)
Case conversion may be inaccurate. Consider using '#align pnat.mul_div_exact PNat.mul_div_exactₓ'. -/
theorem mul_div_exact {m k : ℕ+} (h : k ∣ m) : k * divExact m k = m :=
  by
  apply Eq; rw [mul_coe]
  change (k : ℕ) * (div m k).succ = m
  rw [← div_add_mod m k, dvd_iff'.mp h, Nat.mul_succ]
#align pnat.mul_div_exact PNat.mul_div_exact

#print PNat.dvd_antisymm /-
theorem dvd_antisymm {m n : ℕ+} : m ∣ n → n ∣ m → m = n := fun hmn hnm =>
  (le_of_dvd hmn).antisymm (le_of_dvd hnm)
#align pnat.dvd_antisymm PNat.dvd_antisymm
-/

#print PNat.dvd_one_iff /-
theorem dvd_one_iff (n : ℕ+) : n ∣ 1 ↔ n = 1 :=
  ⟨fun h => dvd_antisymm h (one_dvd n), fun h => h.symm ▸ dvd_refl 1⟩
#align pnat.dvd_one_iff PNat.dvd_one_iff
-/

#print PNat.pos_of_div_pos /-
theorem pos_of_div_pos {n : ℕ+} {a : ℕ} (h : a ∣ n) : 0 < a :=
  by
  apply pos_iff_ne_zero.2
  intro hzero
  rw [hzero] at h
  exact PNat.ne_zero n (eq_zero_of_zero_dvd h)
#align pnat.pos_of_div_pos PNat.pos_of_div_pos
-/

end PNat


import algebra.order.field.basic algebra.big_operators.intervals

/-! # IMO 2015 A1 -/

namespace IMOSL
namespace IMO2015A1

open finset

theorem final_solution {F : Type*} [linear_ordered_field F] {a : ℕ → F}
  (h : ∀ k : ℕ, 0 < a k) (h0 : ∀ k : ℕ, (k.succ : F) * a k / (a k ^ 2 + k) ≤ a k.succ) :
  ∀ n : ℕ, 2 ≤ n → (n : F) ≤ (range n).sum a :=
begin
  ---- First replace the inequality condition on `a`
  replace h0 : ∀ k : ℕ, (k.succ : F) / a k.succ ≤ a k + k / a k :=
  begin
    intros k,
    rw [add_div' _ _ _ (ne_of_gt (h k)), div_le_div_iff (h _) (h k), ← sq],
    exact (div_le_iff' (add_pos_of_pos_of_nonneg (pow_pos (h k) 2) k.cast_nonneg)).mp (h0 k)
  end,

  ---- Now induct on `n`, clearing the easy cases
  apply nat.le_induction,
  rw [nat.cast_bit0, nat.cast_one, sum_range_succ, sum_range_one],
  replace h0 := h0 0,
  rw [nat.cast_one, nat.cast_zero, zero_div, add_zero] at h0,
  refine le_trans _ (add_le_add_right h0 _),
  rw [div_add' _ _ _ (ne_of_gt (h 1)), le_div_iff (h 1), ← sq],
  convert two_mul_le_add_sq 1 (a 1); norm_num,

  intros n h1 h2,
  rw [sum_range_succ, nat.cast_succ],
  cases le_total 1 (a n) with h3 h3,
  exact add_le_add h2 h3,

  ---- The hard cases
  refine le_trans _ (add_le_add_right (_ : (n : F) / a n ≤ _) _),
  rw [div_add' _ _ _ (ne_of_gt (h n)), le_div_iff (h n), add_one_mul,
      ← sub_le_iff_le_add, add_sub_assoc, ← mul_one_sub,
      ← le_sub_iff_add_le', ← mul_one_sub, ← sub_nonneg, ← sub_mul],
  refine mul_nonneg (sub_nonneg.mpr (le_trans h3 _)) (sub_nonneg.mpr h3),
  rw nat.one_le_cast; exact le_trans one_le_two h1,

  clear h1 h2 h3; induction n with n n_ih,
  rw [nat.cast_zero, sum_range_zero, zero_div],
  rw [sum_range_succ, add_comm],
  exact le_trans (h0 n) (add_le_add_left n_ih (a n))
end

end IMO2015A1
end IMOSL


/- Problem 1: Programming in Lean -/

/-
Define the following list functions. Example uses are given via
example. Once you have defined the function (replaced the _ with
an implementation), the examples will work (the red highlighting
will go away).
-/

-- part p1-a
def nonzeros : List Nat -> List Nat :=
fun list => match list with
  | [] => []
  | 0::rest => nonzeros rest
  | first::rest => first::nonzeros rest
example : nonzeros [0,1,0,2,3,0,0] = [1,2,3] := by rfl

def oddmembers : List Nat -> List Nat :=
fun list => match list with
  | [] => []
  | first::rest => if (first % 2 == 1)
             then first::oddmembers rest
             else oddmembers rest
example : oddmembers [0,1,0,2,3,0,0] = [1,3] := by rfl

def countoddmembers : List Nat -> Nat :=
fun list => match list with
  | [] => 0
  | first::rest => if (first % 2 == 1)
             then 1 + countoddmembers rest
             else countoddmembers rest
example : countoddmembers [1,0,3,1,4,5] = 4 := by rfl
example : countoddmembers [0,2,4] = 0 := by rfl
example : countoddmembers [] = 0 := by rfl

-- part p1-a

/- A bag (or multiset) is like a set, except that each element
can appear multiple times rather than just once. One possible
representation for a bag of numbers is as a list.
-/

-- part p1-b
def Bag := List Nat
-- part p1-b

/- Complete the following definitions for the functions count,
union, add, and member for bags.
-/

-- part p1-c
def count : Nat -> Bag -> Nat :=
fun n b => match b with
  | [] => 0
  | first::rest => if (first == n)
             then 1 + count n rest
             else count n rest
example : count 1 [1,2,3,1,4,1] = 3 := by rfl
example : count 6 [1,2,3,1,4,1] = 0 := by rfl

def union : Bag -> Bag -> Bag :=
fun b0 b1 => match b1 with
  | [] => b0
  | first::rest => first::(union b0 rest)
example : count 1 (union [1,2,3] [1,4,1]) = 3 := by rfl

def add : Nat -> Bag -> Bag :=
fun n b => match b with
  | [] => b
  | _ => n::b
example : count 1 (add 1 [1,4,1]) = 3 := by rfl
example : count 5 (add 1 [1,4,1]) = 0 := by rfl

def member : Nat -> Bag -> Bool :=
fun n b => match b with
  | [] => false
  | first::rest => if (first != n)
                   then member n rest
                   else true
example : member 1 [1,4,1] = true := by rfl
example : member 2 [1,4,1] = false := by rfl

def remove_one : Nat -> Bag -> Bag :=
fun n b => match b with
  | [] => []
  | first::rest => if (first == n)
                   then rest
                   else first::(remove_one n rest)
example : count 5 (remove_one 5 [2,1,5,4,1]) = 0 := by rfl
example : count 5 (remove_one 5 [2,1,4,1]) = 0 := by rfl
example : count 4 (remove_one 5 [2,1,4,5,1,4]) = 2 := by rfl
example : count 5 (remove_one 5 [2,1,5,4,5,1,4]) = 1 := by rfl

def remove_all : Nat -> Bag -> Bag :=
fun n b => match b with
  | [] => []
  | first::rest => if (first == n)
                   then remove_all n rest
                   else first::(remove_all n rest)
example : count 5 (remove_all 5 [2,1,5,4,1]) = 0 := by rfl
example : count 5 (remove_all 5 [2,1,4,1]) = 0 := by rfl
example : count 4 (remove_all 5 [2,1,4,5,1,4]) = 2 := by rfl
example : count 5 (remove_all 5 [2,1,5,4,5,1,4,5,1,4]) = 0 := by rfl

def subset : Bag -> Bag -> Bool :=
fun b0 b1 => match b0 with
  | [] => true
  | first::rest => if (member first b1)
                   then subset rest (remove_one first b1)
                   else false
example : subset [1,2] [2,1,4,1] = true := by rfl
example : subset [1,2,2] [2,1,4,1] = false := by rfl

-- part p1-c

/- Proofs in minimal propositional logic -/

-- part p1-d

variable (P Q R S : Prop)

theorem t1 : P -> P := fun P => P

theorem t2 : P -> Q -> P := fun p _ => p

theorem t3 : (P -> Q) -> (Q -> R) -> P -> R := fun PQ QR p => QR (PQ p)

theorem t4 : P -> Q -> (Q -> P -> R) -> R := fun p q PR => PR q p

theorem t5 : (P -> Q) -> (P -> R) -> (R -> Q -> S) -> P -> S :=
  fun PQ PR RQS p => RQS (PR p) (PQ p)

theorem t6 : (P -> Q -> R) -> (P -> Q) -> P -> R :=
  fun PQR PQ p => PQR p (PQ p)

-- part p1-d

/- Proofs in propositional logic -/

-- part p1-e

theorem p1 : P ∧ Q -> Q ∧ P :=
  fun PQ => And.intro PQ.right PQ.left

theorem p2 : P ∧ Q -> P :=
  fun PQ => PQ.left

theorem p3 : P ∧ Q -> (Q -> R) -> R ∧ P :=
  fun PQ QR => And.intro (QR PQ.right) PQ.left

theorem p4 : P ∨ Q -> (P -> R) -> (Q -> R) -> R :=
  fun PQ PR QR => match PQ with
                  | Or.inl p => PR p
                  | Or.inr q => QR q

theorem p5 : P ∨ Q -> (P -> R) -> R ∨ Q :=
  fun PQ PR => match PQ with
              | Or.inl p => Or.inl (PR p)
              | Or.inr q => Or.inr q

theorem p6 : ¬ Q -> (R -> Q) -> (R ∨ ¬ S) -> S -> False :=
  fun NQ RQ RNS S => match RNS with
                    | Or.inl r => False.elim (NQ (RQ r))
                    | Or.inr NS => False.elim (NS S)

-- part p1-e


set_option tactic.simp.trace true
set_option trace.Meta.Tactic.simp.rewrite true

def f (x : α) := x

example (a : α) (b : List α) : f (a::b = []) = False :=
  by simp [f]

def length : List α → Nat
  | []    => 0
  | a::as => length as + 1

example (a b c : α) (as : List α) : length (a :: b :: as) > length as := by
  simp [length]
  apply Nat.lt.step
  apply Nat.lt_succ_self

def fact : Nat → Nat
  | 0 => 1
  | x+1 => (x+1) * fact x

theorem ex3 : fact x > 0 := by
  induction x with
  | zero => decide
  | succ x ih =>
    simp [fact]
    apply Nat.mul_pos
    apply Nat.zero_lt_succ
    apply ih

def head [Inhabited α] : List α → α
  | []   => default
  | a::_ => a

example [Inhabited α] (a : α) (as : List α) : head (a::as) = a :=
  by simp [head]

def foo := 10

example (x : Nat) : foo + x = 10 + x := by
  simp [foo]
  done

def g (x : Nat) : Nat := Id.run <| do
  let x := x
  return x

example : g x = x := by
  simp [g, bind, pure]
  rfl

def f1 : StateM Nat Unit := do
  modify fun x => g x

def f2 : StateM Nat Unit := do
  let s ← get
  set <| g s

example : f1 = f2 := by
  simp [f1, f2, bind, StateT.bind, get, getThe, MonadStateOf.get, StateT.get, pure, set, StateT.set, modify, modifyGet, MonadStateOf.modifyGet, StateT.modifyGet]

def h (x : Nat) : Sum (Nat × Nat) Nat := Sum.inl (x, x)

def bla (x : Nat) :=
  match h x with
  | Sum.inl (y, z) => y + z
  | Sum.inr _ => 0

example (x : Nat) : bla x = x + x := by
  simp [bla, h]

example (x : Nat) (h : 1 ≤ x) : x - 1 + 1 + 2 = x + 2 := by
  simp [h, Nat.sub_add_cancel]

example (x : Nat) : (if h : 1 ≤ x then x - 1 + 1 else 0) = (if _h : 1 ≤ x then x else 0) := by
  simp (config := {contextual := true}) [h, Nat.sub_add_cancel]

theorem my_thm : a ∧ a ↔ a := ⟨fun h => h.1, fun h => ⟨h, h⟩⟩

example : a ∧ (b ∧ b) ↔ a ∧ b := by simp [my_thm]
example : (a ∧ (b ∧ b)) = (a ∧ b) := by simp only [my_thm]

example : x - 1 + 1 = x := by simp (discharger := sorry) [Nat.sub_add_cancel]


/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import topology.vector_bundle
import geometry.manifold.smooth_manifold_with_corners
import data.set.prod

/-!
# Basic smooth bundles

In general, a smooth bundle is a bundle over a smooth manifold, whose fiber is a manifold, and
for which the coordinate changes are smooth. In this definition, there are charts involved at
several places: in the manifold structure of the base, in the manifold structure of the fibers, and
in the local trivializations. This makes it a complicated object in general. There is however a
specific situation where things are much simpler: when the fiber is a vector space (no need for
charts for the fibers), and when the local trivializations of the bundle and the charts of the base
coincide. Then everything is expressed in terms of the charts of the base, making for a much
simpler overall structure, which is easier to manipulate formally.

Most vector bundles that naturally occur in differential geometry are of this form:
the tangent bundle, the cotangent bundle, differential forms (used to define de Rham cohomology)
and the bundle of Riemannian metrics. Therefore, it is worth defining a specific constructor for
this kind of bundle, that we call basic smooth bundles.

A basic smooth bundle is thus a smooth bundle over a smooth manifold whose fiber is a vector space,
and which is trivial in the coordinate charts of the base. (We recall that in our notion of manifold
there is a distinguished atlas, which does not need to be maximal: we require the triviality above
this specific atlas). It can be constructed from a basic smooth bundled core, defined below,
specifying the changes in the fiber when one goes from one coordinate chart to another one.

## Main definitions

* `basic_smooth_vector_bundle_core I M F`: assuming that `M` is a smooth manifold over the model
  with corners `I` on `(𝕜, E, H)`, and `F` is a normed vector space over `𝕜`, this structure
  registers, for each pair of charts of `M`, a linear change of coordinates on `F` depending
  smoothly on the base point. This is the core structure from which one will build a smooth vector
  bundle with fiber `F` over `M`.

Let `Z` be a basic smooth bundle core over `M` with fiber `F`. We define
`Z.to_topological_vector_bundle_core`, the (topological) vector bundle core associated to `Z`. From
it, we get a space `Z.to_topological_vector_bundle_core.total_space` (which as a Type is just
`Σ (x : M), F`), with the fiber bundle topology. It inherits a manifold structure (where the
charts are in bijection with the charts of the basis). We show that this manifold is smooth.

Then we use this machinery to construct the tangent bundle of a smooth manifold.

* `tangent_bundle_core I M`: the basic smooth bundle core associated to a smooth manifold `M` over
  a model with corners `I`.
* `tangent_bundle I M`     : the total space of `tangent_bundle_core I M`. It is itself a
  smooth manifold over the model with corners `I.tangent`, the product of `I` and the trivial model
  with corners on `E`.
* `tangent_space I x`      : the tangent space to `M` at `x`
* `tangent_bundle.proj I M`: the projection from the tangent bundle to the base manifold

## Implementation notes

We register the vector space structure on the fibers of the tangent bundle, but we do not register
the normed space structure coming from that of `F` (as it is not canonical, and we also want to
keep the possibility to add a Riemannian structure on the manifold later on without having two
competing normed space instances on the tangent spaces).

We require `F` to be a normed space, and not just a topological vector space, as we want to talk
about smooth functions on `F`. The notion of derivative requires a norm to be defined.

## TODO
construct the cotangent bundle, and the bundles of differential forms. They should follow
functorially from the description of the tangent bundle as a basic smooth bundle.

## Tags
Smooth fiber bundle, vector bundle, tangent space, tangent bundle
-/
noncomputable theory

universe u

open topological_space set
open_locale manifold topological_space

/-- Core structure used to create a smooth bundle above `M` (a manifold over the model with
corner `I`) with fiber the normed vector space `F` over `𝕜`, which is trivial in the chart domains
of `M`. This structure registers the changes in the fibers when one changes coordinate charts in the
base. We require the change of coordinates of the fibers to be linear, so that the resulting bundle
is a vector bundle. -/
structure basic_smooth_vector_bundle_core {𝕜 : Type*} [nondiscrete_normed_field 𝕜]
{E : Type*} [normed_group E] [normed_space 𝕜 E]
{H : Type*} [topological_space H] (I : model_with_corners 𝕜 E H)
(M : Type*) [topological_space M] [charted_space H M] [smooth_manifold_with_corners I M]
(F : Type*) [normed_group F] [normed_space 𝕜 F] :=
(coord_change      : atlas H M → atlas H M → H → (F →L[𝕜] F))
(coord_change_self : ∀ i : atlas H M, ∀ x ∈ i.1.target, ∀ v, coord_change i i x v = v)
(coord_change_comp : ∀ i j k : atlas H M,
  ∀ x ∈ ((i.1.symm.trans j.1).trans (j.1.symm.trans k.1)).source, ∀ v,
  (coord_change j k ((i.1.symm.trans j.1) x)) (coord_change i j x v) = coord_change i k x v)
(coord_change_smooth_clm : ∀ i j : atlas H M,
  cont_diff_on 𝕜 ∞ ((coord_change i j) ∘ I.symm) (I '' (i.1.symm.trans j.1).source))

/-- The trivial basic smooth bundle core, in which all the changes of coordinates are the
identity. -/
def trivial_basic_smooth_vector_bundle_core {𝕜 : Type*} [nondiscrete_normed_field 𝕜]
{E : Type*} [normed_group E] [normed_space 𝕜 E]
{H : Type*} [topological_space H] (I : model_with_corners 𝕜 E H)
(M : Type*) [topological_space M] [charted_space H M] [smooth_manifold_with_corners I M]
(F : Type*) [normed_group F] [normed_space 𝕜 F] : basic_smooth_vector_bundle_core I M F :=
{ coord_change := λ i j x, continuous_linear_map.id 𝕜 F,
  coord_change_self := λ i x hx v, rfl,
  coord_change_comp := λ i j k x hx v, rfl,
  coord_change_smooth_clm := λ i j, by { dsimp, exact cont_diff_on_const } }

namespace basic_smooth_vector_bundle_core

variables {𝕜 : Type*} [nondiscrete_normed_field 𝕜]
{E : Type*} [normed_group E] [normed_space 𝕜 E]
{H : Type*} [topological_space H] {I : model_with_corners 𝕜 E H}
{M : Type*} [topological_space M] [charted_space H M] [smooth_manifold_with_corners I M]
{F : Type*} [normed_group F] [normed_space 𝕜 F]
(Z : basic_smooth_vector_bundle_core I M F)

instance : inhabited (basic_smooth_vector_bundle_core I M F) :=
⟨trivial_basic_smooth_vector_bundle_core I M F⟩

lemma coord_change_continuous (i j : atlas H M) :
  continuous_on (Z.coord_change i j) (i.1.symm.trans j.1).source :=
begin
  assume x hx,
  apply (((Z.coord_change_smooth_clm i j).continuous_on.continuous_within_at
    (mem_image_of_mem I hx)).comp I.continuous_within_at _).congr,
  { assume y hy,
    simp only with mfld_simps },
  { simp only with mfld_simps },
  { exact maps_to_image I _ },
end

lemma coord_change_smooth (i j : atlas H M) :
  cont_diff_on 𝕜 ∞ (λ p : E × F, Z.coord_change i j (I.symm p.1) p.2)
    ((I '' (i.1.symm.trans j.1).source) ×ˢ (univ : set F)) :=
begin
  have A : cont_diff 𝕜 ∞ (λ p : (F →L[𝕜] F) × F, p.1 p.2),
  { apply is_bounded_bilinear_map.cont_diff,
    exact is_bounded_bilinear_map_apply },
  have B : cont_diff_on 𝕜 ∞ (λ (p : E × F), (Z.coord_change i j (I.symm p.1), p.snd))
    ((I '' (i.1.symm.trans j.1).source) ×ˢ (univ : set F)),
  { apply cont_diff_on.prod _ _,
    { exact (Z.coord_change_smooth_clm i j).comp cont_diff_fst.cont_diff_on
       (prod_subset_preimage_fst _ _) },
    { exact is_bounded_linear_map.snd.cont_diff.cont_diff_on } },
  exact A.comp_cont_diff_on B,
end

/-- Vector bundle core associated to a basic smooth bundle core -/
def to_topological_vector_bundle_core : topological_vector_bundle_core 𝕜 M F (atlas H M) :=
{ base_set := λ i, i.1.source,
  is_open_base_set := λ i, i.1.open_source,
  index_at := λ x, ⟨chart_at H x, chart_mem_atlas H x⟩,
  mem_base_set_at := λ x, mem_chart_source H x,
  coord_change := λ i j x, Z.coord_change i j (i.1 x),
  coord_change_self := λ i x hx v, Z.coord_change_self i (i.1 x) (i.1.map_source hx) v,
  coord_change_comp := λ i j k x ⟨⟨hx1, hx2⟩, hx3⟩ v, begin
    have := Z.coord_change_comp i j k (i.1 x) _ v,
    convert this using 2,
    { simp only [hx1] with mfld_simps },
    { simp only [hx1, hx2, hx3] with mfld_simps }
  end,
  coord_change_continuous := λ i j, begin
    refine ((Z.coord_change_continuous i j).comp' i.1.continuous_on).mono _,
    rintros p ⟨hp₁, hp₂⟩,
    refine ⟨hp₁, i.1.maps_to hp₁, _⟩,
    simp only [i.1.left_inv hp₁, hp₂] with mfld_simps
  end }

@[simp, mfld_simps] lemma base_set (i : atlas H M) :
  (Z.to_topological_vector_bundle_core.local_triv i).base_set = i.1.source := rfl

@[simp, mfld_simps] lemma target (i : atlas H M) :
  (Z.to_topological_vector_bundle_core.local_triv i).target = i.1.source ×ˢ (univ : set F) := rfl

/-- Local chart for the total space of a basic smooth bundle -/
def chart {e : local_homeomorph M H} (he : e ∈ atlas H M) :
  local_homeomorph (Z.to_topological_vector_bundle_core.total_space) (model_prod H F) :=
(Z.to_topological_vector_bundle_core.local_triv ⟨e, he⟩).to_local_homeomorph.trans
  (local_homeomorph.prod e (local_homeomorph.refl F))

@[simp, mfld_simps] lemma chart_source (e : local_homeomorph M H) (he : e ∈ atlas H M) :
  (Z.chart he).source = Z.to_topological_vector_bundle_core.proj ⁻¹' e.source :=
by { simp only [chart, mem_prod], mfld_set_tac }

@[simp, mfld_simps] lemma chart_target (e : local_homeomorph M H) (he : e ∈ atlas H M) :
  (Z.chart he).target = e.target ×ˢ (univ : set F) :=
by { simp only [chart], mfld_set_tac }

/-- The total space of a basic smooth bundle is endowed with a charted space structure, where the
charts are in bijection with the charts of the basis. -/
instance to_charted_space :
  charted_space (model_prod H F) Z.to_topological_vector_bundle_core.total_space :=
{ atlas := ⋃(e : local_homeomorph M H) (he : e ∈ atlas H M), {Z.chart he},
  chart_at := λ p, Z.chart (chart_mem_atlas H p.1),
  mem_chart_source := λ p, by simp [mem_chart_source],
  chart_mem_atlas := λ p, begin
    simp only [mem_Union, mem_singleton_iff, chart_mem_atlas],
    exact ⟨chart_at H p.1, chart_mem_atlas H p.1, rfl⟩
  end }

lemma mem_atlas_iff
  (f : local_homeomorph Z.to_topological_vector_bundle_core.total_space (model_prod H F)) :
  f ∈ atlas (model_prod H F) Z.to_topological_vector_bundle_core.total_space ↔
  ∃(e : local_homeomorph M H) (he : e ∈ atlas H M), f = Z.chart he :=
by simp only [atlas, mem_Union, mem_singleton_iff]

@[simp, mfld_simps] lemma mem_chart_source_iff
  (p q : Z.to_topological_vector_bundle_core.total_space) :
  p ∈ (chart_at (model_prod H F) q).source ↔ p.1 ∈ (chart_at H q.1).source :=
by simp only [chart_at] with mfld_simps

@[simp, mfld_simps] lemma mem_chart_target_iff
  (p : H × F) (q : Z.to_topological_vector_bundle_core.total_space) :
  p ∈ (chart_at (model_prod H F) q).target ↔ p.1 ∈ (chart_at H q.1).target :=
by simp only [chart_at] with mfld_simps

@[simp, mfld_simps] lemma coe_chart_at_fst (p q : Z.to_topological_vector_bundle_core.total_space) :
  ((chart_at (model_prod H F) q) p).1 = chart_at H q.1 p.1 := rfl

@[simp, mfld_simps] lemma coe_chart_at_symm_fst
  (p : H × F) (q : Z.to_topological_vector_bundle_core.total_space) :
  ((chart_at (model_prod H F) q).symm p).1 = ((chart_at H q.1).symm : H → M) p.1 := rfl

/-- Smooth manifold structure on the total space of a basic smooth bundle -/
instance to_smooth_manifold :
  smooth_manifold_with_corners (I.prod (𝓘(𝕜, F))) Z.to_topological_vector_bundle_core.total_space :=
begin
  /- We have to check that the charts belong to the smooth groupoid, i.e., they are smooth on their
  source, and their inverses are smooth on the target. Since both objects are of the same kind, it
  suffices to prove the first statement in A below, and then glue back the pieces at the end. -/
  let J := model_with_corners.to_local_equiv (I.prod (𝓘(𝕜, F))),
  have A : ∀ (e e' : local_homeomorph M H) (he : e ∈ atlas H M) (he' : e' ∈ atlas H M),
    cont_diff_on 𝕜 ∞
    (J ∘ ((Z.chart he).symm.trans (Z.chart he')) ∘ J.symm)
    (J.symm ⁻¹' ((Z.chart he).symm.trans (Z.chart he')).source ∩ range J),
  { assume e e' he he',
    have : J.symm ⁻¹' ((chart Z he).symm.trans (chart Z he')).source ∩ range J =
      (I.symm ⁻¹' (e.symm.trans e').source ∩ range I) ×ˢ (univ : set F),
      by { simp only [J, chart, model_with_corners.prod], mfld_set_tac },
    rw this,
    -- check separately that the two components of the coordinate change are smooth
    apply cont_diff_on.prod,
    show cont_diff_on 𝕜 ∞ (λ (p : E × F), (I ∘ e' ∘ e.symm ∘ I.symm) p.1)
         ((I.symm ⁻¹' (e.symm.trans e').source ∩ range I) ×ˢ (univ : set F)),
    { -- the coordinate change on the base is just a coordinate change for `M`, smooth since
      -- `M` is smooth
      have A : cont_diff_on 𝕜 ∞ (I ∘ (e.symm.trans e') ∘ I.symm)
        (I.symm ⁻¹' (e.symm.trans e').source ∩ range I) :=
      (has_groupoid.compatible (cont_diff_groupoid ∞ I) he he').1,
      have B : cont_diff_on 𝕜 ∞ (λ p : E × F, p.1)
        ((I.symm ⁻¹' (e.symm.trans e').source ∩ range I) ×ˢ (univ : set F)) :=
      cont_diff_fst.cont_diff_on,
      exact cont_diff_on.comp A B (prod_subset_preimage_fst _ _) },
    show cont_diff_on 𝕜 ∞ (λ (p : E × F),
      Z.coord_change ⟨chart_at H (e.symm (I.symm p.1)), _⟩ ⟨e', he'⟩
         ((chart_at H (e.symm (I.symm p.1)) : M → H) (e.symm (I.symm p.1)))
      (Z.coord_change ⟨e, he⟩ ⟨chart_at H (e.symm (I.symm p.1)), _⟩
        (e (e.symm (I.symm p.1))) p.2))
      ((I.symm ⁻¹' (e.symm.trans e').source ∩ range I) ×ˢ (univ : set F)),
    { /- The coordinate change in the fiber is more complicated as its definition involves the
      reference chart chosen at each point. However, it appears with its inverse, so using the
      cocycle property one can get rid of it, and then conclude using the smoothness of the
      cocycle as given in the definition of basic smooth bundles. -/
      have := Z.coord_change_smooth ⟨e, he⟩ ⟨e', he'⟩,
      rw I.image_eq at this,
      apply cont_diff_on.congr this,
      rintros ⟨x, v⟩ hx,
      simp only with mfld_simps at hx,
      let f := chart_at H (e.symm (I.symm x)),
      have A : I.symm x ∈ ((e.symm.trans f).trans (f.symm.trans e')).source,
        by simp only [hx.1.1, hx.1.2] with mfld_simps,
      rw e.right_inv hx.1.1,
      have := Z.coord_change_comp ⟨e, he⟩ ⟨f, chart_mem_atlas _ _⟩ ⟨e', he'⟩ (I.symm x) A v,
      simpa only [] using this } },
  refine @smooth_manifold_with_corners.mk _ _ _ _ _ _ _ _ _ _ _ ⟨_⟩,
  assume e₀ e₀' he₀ he₀',
  rcases (Z.mem_atlas_iff _).1 he₀ with ⟨e, he, rfl⟩,
  rcases (Z.mem_atlas_iff _).1 he₀' with ⟨e', he', rfl⟩,
  rw [cont_diff_groupoid, mem_groupoid_of_pregroupoid],
  exact ⟨A e e' he he', A e' e he' he⟩
end

end basic_smooth_vector_bundle_core

section tangent_bundle

variables {𝕜 : Type*} [nondiscrete_normed_field 𝕜]
{E : Type*} [normed_group E] [normed_space 𝕜 E]
{H : Type*} [topological_space H] (I : model_with_corners 𝕜 E H)
(M : Type*) [topological_space M] [charted_space H M] [smooth_manifold_with_corners I M]

/-- Basic smooth bundle core version of the tangent bundle of a smooth manifold `M` modelled over a
model with corners `I` on `(E, H)`. The fibers are equal to `E`, and the coordinate change in the
fiber corresponds to the derivative of the coordinate change in `M`. -/
def tangent_bundle_core : basic_smooth_vector_bundle_core I M E :=
{ coord_change := λ i j x, (fderiv_within 𝕜 (I ∘ j.1 ∘ i.1.symm ∘ I.symm) (range I) (I x)),
  coord_change_smooth_clm := λ i j,
  begin
    rw I.image_eq,
    have A : cont_diff_on 𝕜 ∞
      (I ∘ (i.1.symm.trans j.1) ∘ I.symm)
      (I.symm ⁻¹' (i.1.symm.trans j.1).source ∩ range I) :=
      (has_groupoid.compatible (cont_diff_groupoid ∞ I) i.2 j.2).1,
    have B : unique_diff_on 𝕜 (I.symm ⁻¹' (i.1.symm.trans j.1).source ∩ range I) :=
      I.unique_diff_preimage_source,
    have C : cont_diff_on 𝕜 ∞
      (λ (p : E × E), (fderiv_within 𝕜 (I ∘ j.1 ∘ i.1.symm ∘ I.symm)
            (I.symm ⁻¹' (i.1.symm.trans j.1).source ∩ range I) p.1 : E → E) p.2)
      ((I.symm ⁻¹' (i.1.symm.trans j.1).source ∩ range I) ×ˢ (univ : set E)) :=
      cont_diff_on_fderiv_within_apply A B le_top,
    have D : ∀ x ∈ (I.symm ⁻¹' (i.1.symm.trans j.1).source ∩ range I),
      fderiv_within 𝕜 (I ∘ j.1 ∘ i.1.symm ∘ I.symm)
            (range I) x =
      fderiv_within 𝕜 (I ∘ j.1 ∘ i.1.symm ∘ I.symm)
            (I.symm ⁻¹' (i.1.symm.trans j.1).source ∩ range I) x,
    { assume x hx,
      have N : I.symm ⁻¹' (i.1.symm.trans j.1).source ∈ nhds x :=
        I.continuous_symm.continuous_at.preimage_mem_nhds
          (is_open.mem_nhds (local_homeomorph.open_source _) hx.1),
      symmetry,
      rw inter_comm,
      exact fderiv_within_inter N (I.unique_diff _ hx.2) },
    apply (A.fderiv_within B le_top).congr,
    assume x hx,
    simp only with mfld_simps at hx,
    simp only [hx, D] with mfld_simps,
  end,
  coord_change_self := λ i x hx v, begin
    /- Locally, a self-change of coordinate is just the identity, thus its derivative is the
    identity. One just needs to write this carefully, paying attention to the sets where the
    functions are defined. -/
    have A : I.symm ⁻¹' (i.1.symm.trans i.1).source ∩ range I ∈ 𝓝[range I] (I x),
    { rw inter_comm,
      apply inter_mem_nhds_within,
      apply I.continuous_symm.continuous_at.preimage_mem_nhds
        (is_open.mem_nhds (local_homeomorph.open_source _) _),
      simp only [hx, i.1.map_target] with mfld_simps },
    have B : ∀ᶠ y in 𝓝[range I] (I x),
      (I ∘ i.1 ∘ i.1.symm ∘ I.symm) y = (id : E → E) y,
    { filter_upwards [A] with _ hy,
      rw ← I.image_eq at hy,
      rcases hy with ⟨z, hz⟩,
      simp only with mfld_simps at hz,
      simp only [hz.2.symm, hz.1] with mfld_simps, },
    have C : fderiv_within 𝕜 (I ∘ i.1 ∘ i.1.symm ∘ I.symm) (range I) (I x) =
             fderiv_within 𝕜 (id : E → E) (range I) (I x) :=
      filter.eventually_eq.fderiv_within_eq I.unique_diff_at_image B
      (by simp only [hx] with mfld_simps),
    rw fderiv_within_id I.unique_diff_at_image at C,
    rw C,
    refl
  end,
  coord_change_comp := λ i j u x hx, begin
    /- The cocycle property is just the fact that the derivative of a composition is the product of
    the derivatives. One needs however to check that all the functions one considers are smooth, and
    to pay attention to the domains where these functions are defined, making this proof a little
    bit cumbersome although there is nothing complicated here. -/
    have M : I x ∈
      (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I) :=
    ⟨by simpa only [mem_preimage, model_with_corners.left_inv] using hx, mem_range_self _⟩,
    have U : unique_diff_within_at 𝕜
      (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I) (I x) :=
      I.unique_diff_preimage_source _ M,
    have A : fderiv_within 𝕜 ((I ∘ u.1 ∘ j.1.symm ∘ I.symm) ∘ (I ∘ j.1 ∘ i.1.symm ∘ I.symm))
             (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I)
             (I x)
      = (fderiv_within 𝕜 (I ∘ u.1 ∘ j.1.symm ∘ I.symm)
             (I.symm ⁻¹' (j.1.symm.trans u.1).source ∩ range I)
             ((I ∘ j.1 ∘ i.1.symm ∘ I.symm) (I x))).comp
        (fderiv_within 𝕜 (I ∘ j.1 ∘ i.1.symm ∘ I.symm)
             (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I)
             (I x)),
    { apply fderiv_within.comp _ _ _ _ U,
      show differentiable_within_at 𝕜 (I ∘ j.1 ∘ i.1.symm ∘ I.symm)
        (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I)
        (I x),
      { have A : cont_diff_on 𝕜 ∞
          (I ∘ (i.1.symm.trans j.1) ∘ I.symm)
          (I.symm ⁻¹' (i.1.symm.trans j.1).source ∩ range I) :=
        (has_groupoid.compatible (cont_diff_groupoid ∞ I) i.2 j.2).1,
        have B : differentiable_on 𝕜 (I ∘ j.1 ∘ i.1.symm ∘ I.symm)
          (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I),
        { apply (A.differentiable_on le_top).mono,
          have : ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ⊆
            (i.1.symm.trans j.1).source := inter_subset_left _ _,
          exact inter_subset_inter (preimage_mono this) (subset.refl (range I)) },
        apply B,
        simpa only [] with mfld_simps using hx },
      show differentiable_within_at 𝕜 (I ∘ u.1 ∘ j.1.symm ∘ I.symm)
        (I.symm ⁻¹' (j.1.symm.trans u.1).source ∩ range I)
        ((I ∘ j.1 ∘ i.1.symm ∘ I.symm) (I x)),
      { have A : cont_diff_on 𝕜 ∞
          (I ∘ (j.1.symm.trans u.1) ∘ I.symm)
          (I.symm ⁻¹' (j.1.symm.trans u.1).source ∩ range I) :=
        (has_groupoid.compatible (cont_diff_groupoid ∞ I) j.2 u.2).1,
        apply A.differentiable_on le_top,
        rw [local_homeomorph.trans_source] at hx,
        simp only with mfld_simps,
        exact hx.2 },
      show (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I)
        ⊆ (I ∘ j.1 ∘ i.1.symm ∘ I.symm) ⁻¹' (I.symm ⁻¹' (j.1.symm.trans u.1).source ∩ range I),
      { assume y hy,
        simp only with mfld_simps at hy,
        rw [local_homeomorph.left_inv] at hy,
        { simp only [hy] with mfld_simps },
        { exact hy.1.1.2 } } },
    have B : fderiv_within 𝕜 ((I ∘ u.1 ∘ j.1.symm ∘ I.symm)
                          ∘ (I ∘ j.1 ∘ i.1.symm ∘ I.symm))
             (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I)
             (I x)
             = fderiv_within 𝕜 (I ∘ u.1 ∘ i.1.symm ∘ I.symm)
             (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I)
             (I x),
    { have E :
        ∀ y ∈ (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I),
          ((I ∘ u.1 ∘ j.1.symm ∘ I.symm) ∘ (I ∘ j.1 ∘ i.1.symm ∘ I.symm)) y =
            (I ∘ u.1 ∘ i.1.symm ∘ I.symm) y,
      { assume y hy,
        simp only [function.comp_app, model_with_corners.left_inv],
        rw [j.1.left_inv],
        exact hy.1.1.2 },
      exact fderiv_within_congr U E (E _ M) },
    have C : fderiv_within 𝕜 (I ∘ u.1 ∘ i.1.symm ∘ I.symm)
             (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I)
             (I x) =
             fderiv_within 𝕜 (I ∘ u.1 ∘ i.1.symm ∘ I.symm)
             (range I) (I x),
    { rw inter_comm,
      apply fderiv_within_inter _ I.unique_diff_at_image,
      apply I.continuous_symm.continuous_at.preimage_mem_nhds
        (is_open.mem_nhds (local_homeomorph.open_source _) _),
      simpa only [model_with_corners.left_inv] using hx },
    have D : fderiv_within 𝕜 (I ∘ u.1 ∘ j.1.symm ∘ I.symm)
      (I.symm ⁻¹' (j.1.symm.trans u.1).source ∩ range I) ((I ∘ j.1 ∘ i.1.symm ∘ I.symm) (I x)) =
      fderiv_within 𝕜 (I ∘ u.1 ∘ j.1.symm ∘ I.symm) (range I) ((I ∘ j.1 ∘ i.1.symm ∘ I.symm) (I x)),
    { rw inter_comm,
      apply fderiv_within_inter _ I.unique_diff_at_image,
      apply I.continuous_symm.continuous_at.preimage_mem_nhds
        (is_open.mem_nhds (local_homeomorph.open_source _) _),
      rw [local_homeomorph.trans_source] at hx,
      simp only with mfld_simps,
      exact hx.2 },
    have E : fderiv_within 𝕜 (I ∘ j.1 ∘ i.1.symm ∘ I.symm)
               (I.symm ⁻¹' ((i.1.symm.trans j.1).trans (j.1.symm.trans u.1)).source ∩ range I)
               (I x) =
             fderiv_within 𝕜 (I ∘ j.1 ∘ i.1.symm ∘ I.symm) (range I) (I x),
    { rw inter_comm,
      apply fderiv_within_inter _ I.unique_diff_at_image,
      apply I.continuous_symm.continuous_at.preimage_mem_nhds
        (is_open.mem_nhds (local_homeomorph.open_source _) _),
      simpa only [model_with_corners.left_inv] using hx },
    rw [B, C, D, E] at A,
    simp only [A, continuous_linear_map.coe_comp'] with mfld_simps,
  end }

variable {M}
include I

/-- The tangent space at a point of the manifold `M`. It is just `E`. We could use instead
`(tangent_bundle_core I M).to_topological_vector_bundle_core.fiber x`, but we use `E` to help the
kernel.
-/
@[nolint unused_arguments]
def tangent_space (x : M) : Type* := E

omit I
variable (M)

/-- The tangent bundle to a smooth manifold, as a Sigma type. Defined in terms of
`bundle.total_space` to be able to put a suitable topology on it. -/
@[nolint has_inhabited_instance, reducible] -- is empty if the base manifold is empty
def tangent_bundle := bundle.total_space (tangent_space I : M → Type*)

local notation `TM` := tangent_bundle I M

/-- The projection from the tangent bundle of a smooth manifold to the manifold. As the tangent
bundle is represented internally as a sigma type, the notation `p.1` also works for the projection
of the point `p`. -/
def tangent_bundle.proj : TM → M :=
λ p, p.1

variable {M}

@[simp, mfld_simps] lemma tangent_bundle.proj_apply (x : M) (v : tangent_space I x) :
  tangent_bundle.proj I M ⟨x, v⟩ = x :=
rfl

section tangent_bundle_instances

/- In general, the definition of tangent_bundle and tangent_space are not reducible, so that type
class inference does not pick wrong instances. In this section, we record the right instances for
them, noting in particular that the tangent bundle is a smooth manifold. -/

section
local attribute [reducible] tangent_space

variables {M} (x : M)

instance : topological_space (tangent_space I x) := by apply_instance
instance : add_comm_group (tangent_space I x) := by apply_instance
instance : topological_add_group (tangent_space I x) := by apply_instance
instance : module 𝕜 (tangent_space I x) := by apply_instance
instance : inhabited (tangent_space I x) := ⟨0⟩

end

variable (M)

instance : topological_space TM :=
(tangent_bundle_core I M).to_topological_vector_bundle_core.to_topological_space (atlas H M)

instance : charted_space (model_prod H E) TM :=
(tangent_bundle_core I M).to_charted_space

instance : smooth_manifold_with_corners I.tangent TM :=
(tangent_bundle_core I M).to_smooth_manifold

instance : topological_vector_bundle 𝕜 E (tangent_space I : M → Type*) :=
topological_vector_bundle_core.fiber.topological_vector_bundle
  (tangent_bundle_core I M).to_topological_vector_bundle_core

end tangent_bundle_instances

variable (M)

/-- The tangent bundle projection on the basis is a continuous map. -/
lemma tangent_bundle_proj_continuous : continuous (tangent_bundle.proj I M) :=
((tangent_bundle_core I M).to_topological_vector_bundle_core).continuous_proj

/-- The tangent bundle projection on the basis is an open map. -/
lemma tangent_bundle_proj_open : is_open_map (tangent_bundle.proj I M) :=
((tangent_bundle_core I M).to_topological_vector_bundle_core).is_open_map_proj

/-- In the tangent bundle to the model space, the charts are just the canonical identification
between a product type and a sigma type, a.k.a. `equiv.sigma_equiv_prod`. -/
@[simp, mfld_simps] lemma tangent_bundle_model_space_chart_at (p : tangent_bundle I H) :
  (chart_at (model_prod H E) p).to_local_equiv = (equiv.sigma_equiv_prod H E).to_local_equiv :=
begin
  have A : ∀ x_fst, fderiv_within 𝕜 (I ∘ I.symm) (range I) (I x_fst) = continuous_linear_map.id 𝕜 E,
  { assume x_fst,
    have : fderiv_within 𝕜 (I ∘ I.symm) (range I) (I x_fst)
         = fderiv_within 𝕜 id (range I) (I x_fst),
    { refine fderiv_within_congr I.unique_diff_at_image (λ y hy, _) (by simp),
      exact model_with_corners.right_inv _ hy },
    rwa fderiv_within_id I.unique_diff_at_image at this },
  ext x : 1,
  show (chart_at (model_prod H E) p : tangent_bundle I H → model_prod H E) x =
    (equiv.sigma_equiv_prod H E) x,
  { cases x,
    simp only [chart_at, basic_smooth_vector_bundle_core.chart, tangent_bundle_core,
      basic_smooth_vector_bundle_core.to_topological_vector_bundle_core, A, prod.mk.inj_iff,
      continuous_linear_map.coe_id'] with mfld_simps,
      exact (tangent_bundle_core I H).coord_change_self _ _ trivial x_snd, },
  show ∀ x, ((chart_at (model_prod H E) p).to_local_equiv).symm x =
    (equiv.sigma_equiv_prod H E).symm x,
  { rintros ⟨x_fst, x_snd⟩,
    simp only [basic_smooth_vector_bundle_core.to_topological_vector_bundle_core,
      tangent_bundle_core, A, continuous_linear_map.coe_id', basic_smooth_vector_bundle_core.chart,
      chart_at, continuous_linear_map.coe_coe, sigma.mk.inj_iff] with mfld_simps, },
  show ((chart_at (model_prod H E) p).to_local_equiv).source = univ,
    by simp only [chart_at] with mfld_simps,
end

@[simp, mfld_simps] lemma tangent_bundle_model_space_coe_chart_at (p : tangent_bundle I H) :
  ⇑(chart_at (model_prod H E) p) = equiv.sigma_equiv_prod H E :=
by { unfold_coes, simp only with mfld_simps }

@[simp, mfld_simps] lemma tangent_bundle_model_space_coe_chart_at_symm (p : tangent_bundle I H) :
  ((chart_at (model_prod H E) p).symm : model_prod H E → tangent_bundle I H) =
  (equiv.sigma_equiv_prod H E).symm :=
by { unfold_coes, simp only with mfld_simps }

variable (H)
/-- The canonical identification between the tangent bundle to the model space and the product,
as a homeomorphism -/
def tangent_bundle_model_space_homeomorph : tangent_bundle I H ≃ₜ model_prod H E :=
{ continuous_to_fun :=
  begin
    let p : tangent_bundle I H := ⟨I.symm (0 : E), (0 : E)⟩,
    have : continuous (chart_at (model_prod H E) p),
    { rw continuous_iff_continuous_on_univ,
      convert local_homeomorph.continuous_on _,
      simp only with mfld_simps },
    simpa only with mfld_simps using this,
  end,
  continuous_inv_fun :=
  begin
    let p : tangent_bundle I H := ⟨I.symm (0 : E), (0 : E)⟩,
    have : continuous (chart_at (model_prod H E) p).symm,
    { rw continuous_iff_continuous_on_univ,
      convert local_homeomorph.continuous_on _,
      simp only with mfld_simps },
    simpa only with mfld_simps using this,
  end,
  .. equiv.sigma_equiv_prod H E }

@[simp, mfld_simps] lemma tangent_bundle_model_space_homeomorph_coe :
  (tangent_bundle_model_space_homeomorph H I : tangent_bundle I H → model_prod H E)
  = equiv.sigma_equiv_prod H E :=
rfl

@[simp, mfld_simps] lemma tangent_bundle_model_space_homeomorph_coe_symm :
  ((tangent_bundle_model_space_homeomorph H I).symm : model_prod H E → tangent_bundle I H)
  = (equiv.sigma_equiv_prod H E).symm :=
rfl

end tangent_bundle


/-
Copyright (c) 2022 Thomas Browning. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Thomas Browning
-/
import group_theory.abelianization
import group_theory.group_action.conj_act
import group_theory.index

/-!
# Commuting Probability
This file introduces the commuting probability of finite groups.

## Main definitions
* `comm_prob`: The commuting probability of a finite type with a multiplication operation.

## Todo
* Neumann's theorem.
-/

noncomputable theory
open_locale classical
open_locale big_operators

open fintype

variables (M : Type*) [fintype M] [has_mul M]

/-- The commuting probability of a finite type with a multiplication operation -/
def comm_prob : ℚ := card {p : M × M // p.1 * p.2 = p.2 * p.1} / card M ^ 2

lemma comm_prob_def : comm_prob M = card {p : M × M // p.1 * p.2 = p.2 * p.1} / card M ^ 2 :=
rfl

lemma comm_prob_pos [h : nonempty M] : 0 < comm_prob M :=
h.elim (λ x, div_pos (nat.cast_pos.mpr (card_pos_iff.mpr ⟨⟨(x, x), rfl⟩⟩))
  (pow_pos (nat.cast_pos.mpr card_pos) 2))

lemma comm_prob_le_one : comm_prob M ≤ 1 :=
begin
  refine div_le_one_of_le _ (sq_nonneg (card M)),
  rw [←nat.cast_pow, nat.cast_le, sq, ←card_prod],
  apply set_fintype_card_le_univ,
end

variables {M}

lemma comm_prob_eq_one_iff [h : nonempty M] : comm_prob M = 1 ↔ commutative ((*) : M → M → M) :=
begin
  change (card {p : M × M | p.1 * p.2 = p.2 * p.1} : ℚ) / _ = 1 ↔ _,
  rw [div_eq_one_iff_eq, ←nat.cast_pow, nat.cast_inj, sq, ←card_prod,
      set_fintype_card_eq_univ_iff, set.eq_univ_iff_forall],
  { exact ⟨λ h x y, h (x, y), λ h x, h x.1 x.2⟩ },
  { exact pow_ne_zero 2 (nat.cast_ne_zero.mpr card_ne_zero) },
end

variables (G : Type*) [group G] [fintype G]

lemma card_comm_eq_card_conj_classes_mul_card :
  card {p : G × G // p.1 * p.2 = p.2 * p.1} = card (conj_classes G) * card G :=
calc card {p : G × G // p.1 * p.2 = p.2 * p.1} = card (Σ g, {h // g * h = h * g}) :
  card_congr (equiv.subtype_prod_equiv_sigma_subtype (λ g h : G, g * h = h * g))
... = ∑ g, card {h // g * h = h * g} : card_sigma _
... = ∑ g, card (mul_action.fixed_by (conj_act G) G g) : sum_equiv conj_act.to_conj_act.to_equiv
  _ _ (λ g, card_congr' $ congr_arg _ $ funext $ λ h, mul_inv_eq_iff_eq_mul.symm.to_eq)
... = card (quotient (mul_action.orbit_rel (conj_act G) G)) * card G :
  mul_action.sum_card_fixed_by_eq_card_orbits_mul_card_group (conj_act G) G
... = card (quotient (is_conj.setoid G)) * card G :
  have this : mul_action.orbit_rel (conj_act G) G = is_conj.setoid G :=
    setoid.ext (λ g h, (setoid.comm' _).trans is_conj_iff.symm),
  by cc

lemma comm_prob_def' : comm_prob G = card (conj_classes G) / card G :=
begin
  rw [comm_prob, card_comm_eq_card_conj_classes_mul_card, nat.cast_mul, sq],
  exact mul_div_mul_right (card (conj_classes G)) (card G) (nat.cast_ne_zero.mpr card_ne_zero),
end

variables {G} (H : subgroup G)

lemma subgroup.comm_prob_subgroup_le : comm_prob H ≤ comm_prob G * H.index ^ 2 :=
begin
  /- After rewriting with `comm_prob_def`, we reduce to showing that `G` has at least as many
    commuting pairs as `H`. -/
  rw [comm_prob_def, comm_prob_def, div_le_iff, mul_assoc, ←mul_pow, ←nat.cast_mul,
      H.index_mul_card, div_mul_cancel, nat.cast_le],
  { apply card_le_of_injective _ _,
    exact λ p, ⟨⟨p.1.1, p.1.2⟩, subtype.ext_iff.mp p.2⟩,
    exact λ p q h, by simpa only [subtype.ext_iff, prod.ext_iff] using h },
  { exact pow_ne_zero 2 (nat.cast_ne_zero.mpr card_ne_zero) },
  { exact pow_pos (nat.cast_pos.mpr card_pos) 2 },
end

lemma subgroup.comm_prob_quotient_le [H.normal] : comm_prob (G ⧸ H) ≤ comm_prob G * card H :=
begin
  /- After rewriting with `comm_prob_def'`, we reduce to showing that `G` has at least as many
    conjugacy classes as `G ⧸ H`. -/
  rw [comm_prob_def', comm_prob_def', div_le_iff, mul_assoc, ←nat.cast_mul, mul_comm (card H),
      ←subgroup.card_eq_card_quotient_mul_card_subgroup, div_mul_cancel, nat.cast_le],
  { exact card_le_of_surjective (conj_classes.map (quotient_group.mk' H))
      (conj_classes.map_surjective quotient.surjective_quotient_mk') },
  { exact nat.cast_ne_zero.mpr card_ne_zero },
  { exact nat.cast_pos.mpr card_pos },
end

variables (G)

lemma inv_card_commutator_le_comm_prob : (↑(card (commutator G)))⁻¹ ≤ comm_prob G :=
(inv_pos_le_iff_one_le_mul (by exact nat.cast_pos.mpr card_pos)).mpr
  (le_trans (ge_of_eq (comm_prob_eq_one_iff.mpr (abelianization.comm_group G).mul_comm))
    (commutator G).comm_prob_quotient_le)