import M4R.Set.Finite.Finset

namespace M4R

  variable (f : α → β → α) (hrcomm : ∀ (a : α) (b₁ b₂ : β), f (f a b₂) b₁ = f (f a b₁) b₂)

  variable (op : α → α → α) (hcomm : ∀ a₁ a₂, op a₁ a₂ = op a₂ a₁) (hassoc : ∀ a₁ a₂ a₃, op (op a₁ a₂) a₃ = op a₁ (op a₂ a₃))
  local infix:55 " ⋆ " => op

  theorem hrcomm_of_comm_assoc {op : α → α → α} (hcomm : ∀ a₁ a₂, op a₁ a₂ = op a₂ a₁)
    (hassoc : ∀ a₁ a₂ a₃, op (op a₁ a₂) a₃ = op a₁ (op a₂ a₃)) :
      ∀ (a b c : α), op (op a c) b = op (op a b) c := fun a b c => by rw [hassoc, hcomm c, ←hassoc]

  namespace UnorderedList

    def fold (init : α) (s : UnorderedList β) : α :=
      Quot.liftOn s (List.foldl f init) fun _ _ p => by
        induction p generalizing init with
        | nil => rfl
        | cons x _ h => exact h (f init x)
        | swap x y _ => simp only [List.foldl]; rw [hrcomm init x y]
        | trans _ _ h₁₂ h₂₃ => exact Eq.trans (h₁₂ init) (h₂₃ init)

    namespace fold

      @[simp] theorem empty (init : α) : fold f hrcomm init 0 = init := rfl

      @[simp] theorem singleton (init : α) (b : β) : fold f hrcomm init (UnorderedList.singleton b) = f init b := rfl

      @[simp] theorem double (init : α) (b c : β) : fold f hrcomm init [b, c] = f (f init b) c := rfl

      theorem append (init : α) (s t : UnorderedList β) :
        fold f hrcomm init (s + t) = fold f hrcomm (fold f hrcomm init s) t :=
          @Quotient.inductionOn₂ (List β) (List β) (Perm.PermSetoid β) (Perm.PermSetoid β)
            (fun (a b : UnorderedList β) => fold f hrcomm init (a + b) = fold f hrcomm (fold f hrcomm init a) b) s t
            (fun a b => List.foldl_append f _ a b)

      theorem cons (init : α) (x : β) (s : UnorderedList β) :
        fold f hrcomm init (s.cons x) = fold f hrcomm (f init x) s :=
          @Quotient.inductionOn (List β) (Perm.PermSetoid β) (fun (ms : UnorderedList β) =>
            fold f hrcomm init (ms.cons x) = fold f hrcomm (f init x) ms) s (fun l => rfl)

      theorem cons' (init : α) (x : β) (s : UnorderedList β) :
        fold f hrcomm init (s.cons x) = f (fold f hrcomm init s) x := by
          rw [cons, ←singleton f hrcomm _ x, ←singleton f hrcomm _ x, ←append, ←append, append.comm]

      theorem fold_add (a₁ a₂ : α) (s₁ s₂ : UnorderedList α) :
          (s₁ + s₂).fold op (hrcomm_of_comm_assoc hcomm hassoc) (op a₁ a₂) =
            (s₁.fold op (hrcomm_of_comm_assoc hcomm hassoc) a₁) ⋆
              (s₂.fold op (hrcomm_of_comm_assoc hcomm hassoc) a₂) :=
                @Quotient.inductionOn (List α) (Perm.PermSetoid α) (fun (l : UnorderedList α) =>
                  (s₁ + l).fold op (hrcomm_of_comm_assoc hcomm hassoc) (a₁ ⋆ a₂) =
                    (s₁.fold op (hrcomm_of_comm_assoc hcomm hassoc) a₁) ⋆
                    (l.fold op (hrcomm_of_comm_assoc hcomm hassoc) a₂))
                  s₂ (fun l => by
                    induction l with
                    | nil => simp; rw [←cons, cons']
                    | cons x l ih => simp at ih ⊢; rw [append.cons_over_right, cons', ih, cons', hassoc])

    end fold

    def map_fold (op : α → α → α) (hcomm : ∀ a₁ a₂, op a₁ a₂ = op a₂ a₁) (hassoc : ∀ a₁ a₂ a₃, op (op a₁ a₂) a₃ = op a₁ (op a₂ a₃))
      (init : α) (f : β → α) (s : UnorderedList β) : α :=
        (s.map f).fold op (hrcomm_of_comm_assoc hcomm hassoc) init

    namespace map_fold

      @[simp] theorem empty (init : α) (f : β → α) : map_fold op hcomm hassoc init f 0 = init := rfl

      theorem congr_map (init : α) {s : UnorderedList β} {f g : β → α} (H : ∀ x ∈ s, f x = g x) :
        s.map_fold op hcomm hassoc init f = s.map_fold op hcomm hassoc init g := by
          simp only [map_fold]
          rw [map.congr rfl H]

      theorem cons (init : α) (f : β → α) (s : UnorderedList β) (b : β):
        (s.cons b).map_fold op hcomm hassoc init f = (s.map_fold op hcomm hassoc init f) ⋆ (f b) := by
          simp [map_fold, fold.cons']

      theorem distrib (init : α) (f g : β → α) (s : UnorderedList β) :
        init ⋆ s.map_fold op hcomm hassoc init (fun b => f b ⋆ g b) =
          (s.map_fold op hcomm hassoc init f) ⋆ (s.map_fold op hcomm hassoc init g) :=
            @Quotient.inductionOn (List β) (Perm.PermSetoid β) (fun (l : UnorderedList β) =>
              init ⋆ l.map_fold op hcomm hassoc init (fun b => f b ⋆ g b) =
                (l.map_fold op hcomm hassoc init f) ⋆ (l.map_fold op hcomm hassoc init g)) s
              (fun l => by
                induction l with
                | nil => simp
                | cons x l ih =>
                  simp [cons]; simp only [list_coe_eq] at ih
                  (conv => rhs; rw [hassoc, ←hassoc (f x), hcomm (f x), hassoc _ (f x), ←hassoc])
                  rw [←ih, hassoc])

      theorem append (init : α) (f : β → α) (s t : UnorderedList β) :
        (s + t).map_fold op hcomm hassoc init f  = t.map_fold op hcomm hassoc (s.map_fold op hcomm hassoc init f) f := by
          have := fold.append op (hrcomm_of_comm_assoc hcomm hassoc) init (s.map f) (t.map f)
          rw [←map.add] at this; exact this

    end map_fold
  end UnorderedList
  namespace Finset

    def fold (init : α) (s : Finset β) : α := UnorderedList.fold f hrcomm init s.elems

    namespace fold

      @[simp] theorem empty (init : α) : fold f hrcomm init ∅ = init := rfl

      @[simp] theorem singleton (init : α) (b : β) : (Finset.singleton b).fold f hrcomm init = f init b := rfl

      @[simp] theorem fold_insert (init : α) {a : β} {s : Finset β} (h : a ∉ s) :
        (insert a s).fold f hrcomm init = f (s.fold f hrcomm init) a := by
          simp only [fold]; rw [insert_val, UnorderedList.ndinsert_of_not_mem h, UnorderedList.fold.cons']

    end fold

    def map_fold (op : α → α → α) (hcomm : ∀ a₁ a₂, op a₁ a₂ = op a₂ a₁) (hassoc : ∀ a₁ a₂ a₃, op (op a₁ a₂) a₃ = op a₁ (op a₂ a₃))
      (init : α) (f : β → α) (s : Finset β) : α :=
        UnorderedList.map_fold op hcomm hassoc init f s.elems

    namespace map_fold

      @[simp] theorem empty (init : α) (f : β → α) : map_fold op hcomm hassoc init f ∅ = init := rfl

      theorem congr_map (init : α) {s : Finset β} {f g : β → α} (h : ∀ x ∈ s, f x = g x) :
        s.map_fold op hcomm hassoc init f = s.map_fold op hcomm hassoc init g :=
          UnorderedList.map_fold.congr_map op hcomm hassoc init h

      theorem union_inter (f : β → α) {s₁ s₂ : Finset β} {a₁ a₂ : α} :
        (s₁ ∪ s₂).map_fold op hcomm hassoc a₁ f ⋆ (s₁ ∩ s₂).map_fold op hcomm hassoc a₂ f =
          s₁.map_fold op hcomm hassoc a₂ f ⋆ s₂.map_fold op hcomm hassoc a₁ f := by
            simp only [map_fold, UnorderedList.map_fold]
            rw [←UnorderedList.fold.fold_add op hcomm hassoc, ←UnorderedList.map.add,
              Finset.union_val, Finset.inter_val, UnorderedList.union_add_inter,
              UnorderedList.map.add, hcomm, UnorderedList.fold.fold_add op hcomm hassoc]

      @[simp] theorem fold_insert (init : α) (f : β → α) {a : β} {s : Finset β} (h : a ∉ s) :
        (insert a s).map_fold op hcomm hassoc init f = f a ⋆ s.map_fold op hcomm hassoc init f := by
          simp only [map_fold, UnorderedList.map_fold]
          rw [insert_val, UnorderedList.ndinsert_of_not_mem h, UnorderedList.map.cons, UnorderedList.fold.cons', hcomm]

    end map_fold
  end Finset
end M4R


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

! This file was ported from Lean 3 source module set_theory.cardinal.divisibility
! leanprover-community/mathlib commit ee05e9ce1322178f0c12004eb93c00d2c8c00ed2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.IsPrimePow
import Mathbin.SetTheory.Cardinal.Ordinal

/-!
# Cardinal Divisibility

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

We show basic results about divisibility in the cardinal numbers. This relation can be characterised
in the following simple way: if `a` and `b` are both less than `ℵ₀`, then `a ∣ b` iff they are
divisible as natural numbers. If `b` is greater than `ℵ₀`, then `a ∣ b` iff `a ≤ b`. This
furthermore shows that all infinite cardinals are prime; recall that `a * b = max a b` if
`ℵ₀ ≤ a * b`; therefore `a ∣ b * c = a ∣ max b c` and therefore clearly either `a ∣ b` or `a ∣ c`.
Note furthermore that no infinite cardinal is irreducible
(`cardinal.not_irreducible_of_aleph_0_le`), showing that the cardinal numbers do not form a
`comm_cancel_monoid_with_zero`.

## Main results

* `cardinal.prime_of_aleph_0_le`: a `cardinal` is prime if it is infinite.
* `cardinal.is_prime_iff`: a `cardinal` is prime iff it is infinite or a prime natural number.
* `cardinal.is_prime_pow_iff`: a `cardinal` is a prime power iff it is infinite or a natural number
  which is itself a prime power.

-/

namespace Cardinal

open Cardinal

universe u

variable {a b : Cardinal.{u}} {n m : ℕ}

/- warning: cardinal.is_unit_iff -> Cardinal.isUnit_iff is a dubious translation:
lean 3 declaration is
  forall {a : Cardinal.{u1}}, Iff (IsUnit.{succ u1} Cardinal.{u1} (MonoidWithZero.toMonoid.{succ u1} Cardinal.{u1} (Semiring.toMonoidWithZero.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.canonicallyOrderedCommSemiring.{u1}))))) a) (Eq.{succ (succ u1)} Cardinal.{u1} a (OfNat.ofNat.{succ u1} Cardinal.{u1} 1 (OfNat.mk.{succ u1} Cardinal.{u1} 1 (One.one.{succ u1} Cardinal.{u1} Cardinal.hasOne.{u1}))))
but is expected to have type
  forall {a : Cardinal.{u1}}, Iff (IsUnit.{succ u1} Cardinal.{u1} (MonoidWithZero.toMonoid.{succ u1} Cardinal.{u1} (Semiring.toMonoidWithZero.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.instCanonicallyOrderedCommSemiringCardinal.{u1}))))) a) (Eq.{succ (succ u1)} Cardinal.{u1} a (OfNat.ofNat.{succ u1} Cardinal.{u1} 1 (One.toOfNat1.{succ u1} Cardinal.{u1} Cardinal.instOneCardinal.{u1})))
Case conversion may be inaccurate. Consider using '#align cardinal.is_unit_iff Cardinal.isUnit_iffₓ'. -/
@[simp]
theorem isUnit_iff : IsUnit a ↔ a = 1 :=
  by
  refine'
    ⟨fun h => _, by
      rintro rfl
      exact isUnit_one⟩
  rcases eq_or_ne a 0 with (rfl | ha)
  · exact (not_isUnit_zero h).elim
  rw [isUnit_iff_forall_dvd] at h
  cases' h 1 with t ht
  rw [eq_comm, mul_eq_one_iff'] at ht
  · exact ht.1
  all_goals rwa [one_le_iff_ne_zero]
  · rintro rfl
    rw [MulZeroClass.mul_zero] at ht
    exact zero_ne_one ht
#align cardinal.is_unit_iff Cardinal.isUnit_iff

instance : Unique Cardinal.{u}ˣ where
  default := 1
  uniq a := Units.val_eq_one.mp <| isUnit_iff.mp a.IsUnit

#print Cardinal.le_of_dvd /-
theorem le_of_dvd : ∀ {a b : Cardinal}, b ≠ 0 → a ∣ b → a ≤ b
  | a, _, b0, ⟨b, rfl⟩ => by
    simpa only [mul_one] using
      mul_le_mul_left'
        (one_le_iff_ne_zero.2 fun h : b = 0 => by simpa only [h, MulZeroClass.mul_zero] using b0) a
#align cardinal.le_of_dvd Cardinal.le_of_dvd
-/

#print Cardinal.dvd_of_le_of_aleph0_le /-
theorem dvd_of_le_of_aleph0_le (ha : a ≠ 0) (h : a ≤ b) (hb : ℵ₀ ≤ b) : a ∣ b :=
  ⟨b, (mul_eq_right hb h ha).symm⟩
#align cardinal.dvd_of_le_of_aleph_0_le Cardinal.dvd_of_le_of_aleph0_le
-/

#print Cardinal.prime_of_aleph0_le /-
@[simp]
theorem prime_of_aleph0_le (ha : ℵ₀ ≤ a) : Prime a :=
  by
  refine' ⟨(aleph_0_pos.trans_le ha).ne', _, fun b c hbc => _⟩
  · rw [is_unit_iff]
    exact (one_lt_aleph_0.trans_le ha).ne'
  cases' eq_or_ne (b * c) 0 with hz hz
  · rcases mul_eq_zero.mp hz with (rfl | rfl) <;> simp
  wlog h : c ≤ b
  · cases le_total c b <;> [skip, rw [or_comm']] <;> apply_assumption
    assumption'
    all_goals rwa [mul_comm]
  left
  have habc := le_of_dvd hz hbc
  rwa [mul_eq_max' <| ha.trans <| habc, max_def', if_pos h] at hbc
#align cardinal.prime_of_aleph_0_le Cardinal.prime_of_aleph0_le
-/

/- warning: cardinal.not_irreducible_of_aleph_0_le -> Cardinal.not_irreducible_of_aleph0_le is a dubious translation:
lean 3 declaration is
  forall {a : Cardinal.{u1}}, (LE.le.{succ u1} Cardinal.{u1} Cardinal.hasLe.{u1} Cardinal.aleph0.{u1} a) -> (Not (Irreducible.{succ u1} Cardinal.{u1} (MonoidWithZero.toMonoid.{succ u1} Cardinal.{u1} (Semiring.toMonoidWithZero.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.canonicallyOrderedCommSemiring.{u1}))))) a))
but is expected to have type
  forall {a : Cardinal.{u1}}, (LE.le.{succ u1} Cardinal.{u1} Cardinal.instLECardinal.{u1} Cardinal.aleph0.{u1} a) -> (Not (Irreducible.{succ u1} Cardinal.{u1} (MonoidWithZero.toMonoid.{succ u1} Cardinal.{u1} (Semiring.toMonoidWithZero.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.instCanonicallyOrderedCommSemiringCardinal.{u1}))))) a))
Case conversion may be inaccurate. Consider using '#align cardinal.not_irreducible_of_aleph_0_le Cardinal.not_irreducible_of_aleph0_leₓ'. -/
theorem not_irreducible_of_aleph0_le (ha : ℵ₀ ≤ a) : ¬Irreducible a :=
  by
  rw [irreducible_iff, not_and_or]
  refine' Or.inr fun h => _
  simpa [mul_aleph_0_eq ha, is_unit_iff, (one_lt_aleph_0.trans_le ha).ne', one_lt_aleph_0.ne'] using
    h a ℵ₀
#align cardinal.not_irreducible_of_aleph_0_le Cardinal.not_irreducible_of_aleph0_le

/- warning: cardinal.nat_coe_dvd_iff -> Cardinal.nat_coe_dvd_iff is a dubious translation:
lean 3 declaration is
  forall {n : Nat} {m : Nat}, Iff (Dvd.Dvd.{succ u1} Cardinal.{u1} (semigroupDvd.{succ u1} Cardinal.{u1} (SemigroupWithZero.toSemigroup.{succ u1} Cardinal.{u1} (NonUnitalSemiring.toSemigroupWithZero.{succ u1} Cardinal.{u1} (NonUnitalCommSemiring.toNonUnitalSemiring.{succ u1} Cardinal.{u1} (CommSemiring.toNonUnitalCommSemiring.{succ u1} Cardinal.{u1} Cardinal.commSemiring.{u1}))))) ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) n) ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) m)) (Dvd.Dvd.{0} Nat Nat.hasDvd n m)
but is expected to have type
  forall {n : Nat} {m : Nat}, Iff (Dvd.dvd.{succ u1} Cardinal.{u1} (semigroupDvd.{succ u1} Cardinal.{u1} (SemigroupWithZero.toSemigroup.{succ u1} Cardinal.{u1} (NonUnitalSemiring.toSemigroupWithZero.{succ u1} Cardinal.{u1} (NonUnitalCommSemiring.toNonUnitalSemiring.{succ u1} Cardinal.{u1} (CommSemiring.toNonUnitalCommSemiring.{succ u1} Cardinal.{u1} Cardinal.commSemiring.{u1}))))) (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} n) (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} m)) (Dvd.dvd.{0} Nat Nat.instDvdNat n m)
Case conversion may be inaccurate. Consider using '#align cardinal.nat_coe_dvd_iff Cardinal.nat_coe_dvd_iffₓ'. -/
@[simp, norm_cast]
theorem nat_coe_dvd_iff : (n : Cardinal) ∣ m ↔ n ∣ m :=
  by
  refine' ⟨_, fun ⟨h, ht⟩ => ⟨h, by exact_mod_cast ht⟩⟩
  rintro ⟨k, hk⟩
  have : ↑m < ℵ₀ := nat_lt_aleph_0 m
  rw [hk, mul_lt_aleph_0_iff] at this
  rcases this with (h | h | ⟨-, hk'⟩)
  iterate 2 simp only [h, MulZeroClass.mul_zero, MulZeroClass.zero_mul, Nat.cast_eq_zero] at hk;
    simp [hk]
  lift k to ℕ using hk'
  exact ⟨k, by exact_mod_cast hk⟩
#align cardinal.nat_coe_dvd_iff Cardinal.nat_coe_dvd_iff

/- warning: cardinal.nat_is_prime_iff -> Cardinal.nat_is_prime_iff is a dubious translation:
lean 3 declaration is
  forall {n : Nat}, Iff (Prime.{succ u1} Cardinal.{u1} (CommSemiring.toCommMonoidWithZero.{succ u1} Cardinal.{u1} Cardinal.commSemiring.{u1}) ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) n)) (Nat.Prime n)
but is expected to have type
  forall {n : Nat}, Iff (Prime.{succ u1} Cardinal.{u1} (CommSemiring.toCommMonoidWithZero.{succ u1} Cardinal.{u1} Cardinal.commSemiring.{u1}) (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} n)) (Nat.Prime n)
Case conversion may be inaccurate. Consider using '#align cardinal.nat_is_prime_iff Cardinal.nat_is_prime_iffₓ'. -/
@[simp]
theorem nat_is_prime_iff : Prime (n : Cardinal) ↔ n.Prime :=
  by
  simp only [Prime, Nat.prime_iff]
  refine' and_congr (by simp) (and_congr _ ⟨fun h b c hbc => _, fun h b c hbc => _⟩)
  · simp only [is_unit_iff, Nat.isUnit_iff]
    exact_mod_cast Iff.rfl
  · exact_mod_cast h b c (by exact_mod_cast hbc)
  cases' lt_or_le (b * c) ℵ₀ with h' h'
  · rcases mul_lt_aleph_0_iff.mp h' with (rfl | rfl | ⟨hb, hc⟩)
    · simp
    · simp
    lift b to ℕ using hb
    lift c to ℕ using hc
    exact_mod_cast h b c (by exact_mod_cast hbc)
  rcases aleph_0_le_mul_iff.mp h' with ⟨hb, hc, hℵ₀⟩
  have hn : (n : Cardinal) ≠ 0 := by
    intro h
    rw [h, zero_dvd_iff, mul_eq_zero] at hbc
    cases hbc <;> contradiction
  wlog hℵ₀b : ℵ₀ ≤ b
  · refine' (this h c b _ _ hc hb hℵ₀.symm hn (hℵ₀.resolve_left hℵ₀b)).symm <;> rwa [mul_comm]
  exact Or.inl (dvd_of_le_of_aleph_0_le hn ((nat_lt_aleph_0 n).le.trans hℵ₀b) hℵ₀b)
#align cardinal.nat_is_prime_iff Cardinal.nat_is_prime_iff

/- warning: cardinal.is_prime_iff -> Cardinal.is_prime_iff is a dubious translation:
lean 3 declaration is
  forall {a : Cardinal.{u1}}, Iff (Prime.{succ u1} Cardinal.{u1} (CommSemiring.toCommMonoidWithZero.{succ u1} Cardinal.{u1} Cardinal.commSemiring.{u1}) a) (Or (LE.le.{succ u1} Cardinal.{u1} Cardinal.hasLe.{u1} Cardinal.aleph0.{u1} a) (Exists.{1} Nat (fun (p : Nat) => And (Eq.{succ (succ u1)} Cardinal.{u1} a ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) p)) (Nat.Prime p))))
but is expected to have type
  forall {a : Cardinal.{u1}}, Iff (Prime.{succ u1} Cardinal.{u1} (CommSemiring.toCommMonoidWithZero.{succ u1} Cardinal.{u1} Cardinal.commSemiring.{u1}) a) (Or (LE.le.{succ u1} Cardinal.{u1} Cardinal.instLECardinal.{u1} Cardinal.aleph0.{u1} a) (Exists.{1} Nat (fun (p : Nat) => And (Eq.{succ (succ u1)} Cardinal.{u1} a (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} p)) (Nat.Prime p))))
Case conversion may be inaccurate. Consider using '#align cardinal.is_prime_iff Cardinal.is_prime_iffₓ'. -/
theorem is_prime_iff {a : Cardinal} : Prime a ↔ ℵ₀ ≤ a ∨ ∃ p : ℕ, a = p ∧ p.Prime :=
  by
  cases' le_or_lt ℵ₀ a with h h
  · simp [h]
  lift a to ℕ using id h
  simp [not_le.mpr h]
#align cardinal.is_prime_iff Cardinal.is_prime_iff

/- warning: cardinal.is_prime_pow_iff -> Cardinal.isPrimePow_iff is a dubious translation:
lean 3 declaration is
  forall {a : Cardinal.{u1}}, Iff (IsPrimePow.{succ u1} Cardinal.{u1} (CommSemiring.toCommMonoidWithZero.{succ u1} Cardinal.{u1} Cardinal.commSemiring.{u1}) a) (Or (LE.le.{succ u1} Cardinal.{u1} Cardinal.hasLe.{u1} Cardinal.aleph0.{u1} a) (Exists.{1} Nat (fun (n : Nat) => And (Eq.{succ (succ u1)} Cardinal.{u1} a ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) n)) (IsPrimePow.{0} Nat (LinearOrderedCommMonoidWithZero.toCommMonoidWithZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) n))))
but is expected to have type
  forall {a : Cardinal.{u1}}, Iff (IsPrimePow.{succ u1} Cardinal.{u1} (CommSemiring.toCommMonoidWithZero.{succ u1} Cardinal.{u1} Cardinal.commSemiring.{u1}) a) (Or (LE.le.{succ u1} Cardinal.{u1} Cardinal.instLECardinal.{u1} Cardinal.aleph0.{u1} a) (Exists.{1} Nat (fun (n : Nat) => And (Eq.{succ (succ u1)} Cardinal.{u1} a (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} n)) (IsPrimePow.{0} Nat (LinearOrderedCommMonoidWithZero.toCommMonoidWithZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) n))))
Case conversion may be inaccurate. Consider using '#align cardinal.is_prime_pow_iff Cardinal.isPrimePow_iffₓ'. -/
theorem isPrimePow_iff {a : Cardinal} : IsPrimePow a ↔ ℵ₀ ≤ a ∨ ∃ n : ℕ, a = n ∧ IsPrimePow n :=
  by
  by_cases h : ℵ₀ ≤ a
  · simp [h, (prime_of_aleph_0_le h).IsPrimePow]
  lift a to ℕ using not_le.mp h
  simp only [h, Nat.cast_inj, exists_eq_left', false_or_iff, isPrimePow_nat_iff]
  rw [isPrimePow_def]
  refine'
    ⟨_, fun ⟨p, k, hp, hk, h⟩ => ⟨p, k, nat_is_prime_iff.2 hp, by exact_mod_cast And.intro hk h⟩⟩
  rintro ⟨p, k, hp, hk, hpk⟩
  have key : _ ≤ p ^ k :=
    power_le_power_left hp.ne_zero (show (1 : Cardinal) ≤ k by exact_mod_cast hk)
  rw [power_one, hpk] at key
  lift p to ℕ using key.trans_lt (nat_lt_aleph_0 a)
  exact ⟨p, k, nat_is_prime_iff.mp hp, hk, by exact_mod_cast hpk⟩
#align cardinal.is_prime_pow_iff Cardinal.isPrimePow_iff

end Cardinal