/-
Copyright (c) 2022 Violeta Hernández Palacios. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Violeta Hernández Palacios

! This file was ported from Lean 3 source module set_theory.ordinal.principal
! leanprover-community/mathlib commit 3dadefa3f544b1db6214777fe47910739b54c66a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.SetTheory.Ordinal.FixedPoint

/-!
### Principal ordinals

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

We define principal or indecomposable ordinals, and we prove the standard properties about them.

### Main definitions and results
* `principal`: A principal or indecomposable ordinal under some binary operation. We include 0 and
  any other typically excluded edge cases for simplicity.
* `unbounded_principal`: Principal ordinals are unbounded.
* `principal_add_iff_zero_or_omega_opow`: The main characterization theorem for additive principal
  ordinals.
* `principal_mul_iff_le_two_or_omega_opow_opow`: The main characterization theorem for
  multiplicative principal ordinals.

### Todo
* Prove that exponential principal ordinals are 0, 1, 2, ω, or epsilon numbers, i.e. fixed points
  of `λ x, ω ^ x`.
-/

universe u

noncomputable section

open Order

namespace Ordinal

-- mathport name: ordinal.pow
local infixr:0 "^" => @pow Ordinal Ordinal Ordinal.hasPow

/-! ### Principal ordinals -/

#print Ordinal.Principal /-
/-- An ordinal `o` is said to be principal or indecomposable under an operation when the set of
ordinals less than it is closed under that operation. In standard mathematical usage, this term is
almost exclusively used for additive and multiplicative principal ordinals.

For simplicity, we break usual convention and regard 0 as principal. -/
def Principal (op : Ordinal → Ordinal → Ordinal) (o : Ordinal) : Prop :=
  ∀ ⦃a b⦄, a < o → b < o → op a b < o
#align ordinal.principal Ordinal.Principal
-/

#print Ordinal.principal_iff_principal_swap /-
theorem principal_iff_principal_swap {op : Ordinal → Ordinal → Ordinal} {o : Ordinal} :
    Principal op o ↔ Principal (Function.swap op) o := by
  constructor <;> exact fun h a b ha hb => h hb ha
#align ordinal.principal_iff_principal_swap Ordinal.principal_iff_principal_swap
-/

#print Ordinal.principal_zero /-
theorem principal_zero {op : Ordinal → Ordinal → Ordinal} : Principal op 0 := fun a _ h =>
  (Ordinal.not_lt_zero a h).elim
#align ordinal.principal_zero Ordinal.principal_zero
-/

/- warning: ordinal.principal_one_iff -> Ordinal.principal_one_iff is a dubious translation:
lean 3 declaration is
  forall {op : Ordinal.{u1} -> Ordinal.{u1} -> Ordinal.{u1}}, Iff (Ordinal.Principal.{u1} op (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1})))) (Eq.{succ (succ u1)} Ordinal.{u1} (op (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1})))) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))))
but is expected to have type
  forall {op : Ordinal.{u1} -> Ordinal.{u1} -> Ordinal.{u1}}, Iff (Ordinal.Principal.{u1} op (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1}))) (Eq.{succ (succ u1)} Ordinal.{u1} (op (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1}))) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})))
Case conversion may be inaccurate. Consider using '#align ordinal.principal_one_iff Ordinal.principal_one_iffₓ'. -/
@[simp]
theorem principal_one_iff {op : Ordinal → Ordinal → Ordinal} : Principal op 1 ↔ op 0 0 = 0 :=
  by
  refine' ⟨fun h => _, fun h a b ha hb => _⟩
  · rwa [← lt_one_iff_zero]
    exact h zero_lt_one zero_lt_one
  · rwa [lt_one_iff_zero, ha, hb] at *
#align ordinal.principal_one_iff Ordinal.principal_one_iff

#print Ordinal.Principal.iterate_lt /-
theorem Principal.iterate_lt {op : Ordinal → Ordinal → Ordinal} {a o : Ordinal} (hao : a < o)
    (ho : Principal op o) (n : ℕ) : (op a^[n]) a < o :=
  by
  induction' n with n hn
  · rwa [Function.iterate_zero]
  · rw [Function.iterate_succ']
    exact ho hao hn
#align ordinal.principal.iterate_lt Ordinal.Principal.iterate_lt
-/

#print Ordinal.op_eq_self_of_principal /-
theorem op_eq_self_of_principal {op : Ordinal → Ordinal → Ordinal} {a o : Ordinal.{u}} (hao : a < o)
    (H : IsNormal (op a)) (ho : Principal op o) (ho' : IsLimit o) : op a o = o :=
  by
  refine' le_antisymm _ (H.self_le _)
  rw [← IsNormal.bsup_eq.{u, u} H ho', bsup_le_iff]
  exact fun b hbo => (ho hao hbo).le
#align ordinal.op_eq_self_of_principal Ordinal.op_eq_self_of_principal
-/

#print Ordinal.nfp_le_of_principal /-
theorem nfp_le_of_principal {op : Ordinal → Ordinal → Ordinal} {a o : Ordinal} (hao : a < o)
    (ho : Principal op o) : nfp (op a) a ≤ o :=
  nfp_le fun n => (ho.iterate_lt hao n).le
#align ordinal.nfp_le_of_principal Ordinal.nfp_le_of_principal
-/

/-! ### Principal ordinals are unbounded -/

#print Ordinal.blsub₂ /-
/-- The least strict upper bound of `op` applied to all pairs of ordinals less than `o`. This is
essentially a two-argument version of `ordinal.blsub`. -/
def blsub₂ (op : Ordinal → Ordinal → Ordinal) (o : Ordinal) : Ordinal :=
  lsub fun x : o.out.α × o.out.α => op (typein (· < ·) x.1) (typein (· < ·) x.2)
#align ordinal.blsub₂ Ordinal.blsub₂
-/

#print Ordinal.lt_blsub₂ /-
theorem lt_blsub₂ (op : Ordinal → Ordinal → Ordinal) {o : Ordinal} {a b : Ordinal} (ha : a < o)
    (hb : b < o) : op a b < blsub₂ op o :=
  by
  convert lt_lsub _
      (Prod.mk (enum (· < ·) a (by rwa [type_lt])) (enum (· < ·) b (by rwa [type_lt])))
  simp only [typein_enum]
#align ordinal.lt_blsub₂ Ordinal.lt_blsub₂
-/

#print Ordinal.principal_nfp_blsub₂ /-
theorem principal_nfp_blsub₂ (op : Ordinal → Ordinal → Ordinal) (o : Ordinal) :
    Principal op (nfp (blsub₂.{u, u} op) o) := fun a b ha hb =>
  by
  rw [lt_nfp] at *
  cases' ha with m hm
  cases' hb with n hn
  cases' le_total ((blsub₂.{u, u} op^[m]) o) ((blsub₂.{u, u} op^[n]) o) with h h
  · use n + 1
    rw [Function.iterate_succ']
    exact lt_blsub₂ op (hm.trans_le h) hn
  · use m + 1
    rw [Function.iterate_succ']
    exact lt_blsub₂ op hm (hn.trans_le h)
#align ordinal.principal_nfp_blsub₂ Ordinal.principal_nfp_blsub₂
-/

#print Ordinal.unbounded_principal /-
theorem unbounded_principal (op : Ordinal → Ordinal → Ordinal) :
    Set.Unbounded (· < ·) { o | Principal op o } := fun o =>
  ⟨_, principal_nfp_blsub₂ op o, (le_nfp _ o).not_lt⟩
#align ordinal.unbounded_principal Ordinal.unbounded_principal
-/

/-! #### Additive principal ordinals -/

/- warning: ordinal.principal_add_one -> Ordinal.principal_add_one is a dubious translation:
lean 3 declaration is
  Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1})))
but is expected to have type
  Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1094 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1096 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1094 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1096) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1}))
Case conversion may be inaccurate. Consider using '#align ordinal.principal_add_one Ordinal.principal_add_oneₓ'. -/
theorem principal_add_one : Principal (· + ·) 1 :=
  principal_one_iff.2 <| zero_add 0
#align ordinal.principal_add_one Ordinal.principal_add_one

/- warning: ordinal.principal_add_of_le_one -> Ordinal.principal_add_of_le_one is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}}, (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) o (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1})))) -> (Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) o)
but is expected to have type
  forall {o : Ordinal.{u1}}, (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) o (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1}))) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1124 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1126 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1124 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1126) o)
Case conversion may be inaccurate. Consider using '#align ordinal.principal_add_of_le_one Ordinal.principal_add_of_le_oneₓ'. -/
theorem principal_add_of_le_one {o : Ordinal} (ho : o ≤ 1) : Principal (· + ·) o :=
  by
  rcases le_one_iff.1 ho with (rfl | rfl)
  · exact principal_zero
  · exact principal_add_one
#align ordinal.principal_add_of_le_one Ordinal.principal_add_of_le_one

/- warning: ordinal.principal_add_is_limit -> Ordinal.principal_add_isLimit is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1}))) o) -> (Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) o) -> (Ordinal.IsLimit.{u1} o)
but is expected to have type
  forall {o : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1})) o) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1166 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1168 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1166 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.1168) o) -> (Ordinal.IsLimit.{u1} o)
Case conversion may be inaccurate. Consider using '#align ordinal.principal_add_is_limit Ordinal.principal_add_isLimitₓ'. -/
theorem principal_add_isLimit {o : Ordinal} (ho₁ : 1 < o) (ho : Principal (· + ·) o) : o.IsLimit :=
  by
  refine' ⟨fun ho₀ => _, fun a hao => _⟩
  · rw [ho₀] at ho₁
    exact not_lt_of_gt zero_lt_one ho₁
  · cases' eq_or_ne a 0 with ha ha
    · rw [ha, succ_zero]
      exact ho₁
    · refine' lt_of_le_of_lt _ (ho hao hao)
      rwa [← add_one_eq_succ, add_le_add_iff_left, one_le_iff_ne_zero]
#align ordinal.principal_add_is_limit Ordinal.principal_add_isLimit

#print Ordinal.principal_add_iff_add_left_eq_self /-
theorem principal_add_iff_add_left_eq_self {o : Ordinal} :
    Principal (· + ·) o ↔ ∀ a < o, a + o = o :=
  by
  refine' ⟨fun ho a hao => _, fun h a b hao hbo => _⟩
  · cases' lt_or_le 1 o with ho₁ ho₁
    · exact op_eq_self_of_principal hao (add_is_normal a) ho (principal_add_is_limit ho₁ ho)
    · rcases le_one_iff.1 ho₁ with (rfl | rfl)
      · exact (Ordinal.not_lt_zero a hao).elim
      · rw [lt_one_iff_zero] at hao
        rw [hao, zero_add]
  · rw [← h a hao]
    exact (add_is_normal a).StrictMono hbo
#align ordinal.principal_add_iff_add_left_eq_self Ordinal.principal_add_iff_add_left_eq_self
-/

#print Ordinal.exists_lt_add_of_not_principal_add /-
theorem exists_lt_add_of_not_principal_add {a} (ha : ¬Principal (· + ·) a) :
    ∃ (b c : _)(hb : b < a)(hc : c < a), b + c = a :=
  by
  unfold principal at ha
  push_neg  at ha
  rcases ha with ⟨b, c, hb, hc, H⟩
  refine'
    ⟨b, _, hb, lt_of_le_of_ne (sub_le_self a b) fun hab => _, Ordinal.add_sub_cancel_of_le hb.le⟩
  rw [← sub_le, hab] at H
  exact H.not_lt hc
#align ordinal.exists_lt_add_of_not_principal_add Ordinal.exists_lt_add_of_not_principal_add
-/

#print Ordinal.principal_add_iff_add_lt_ne_self /-
theorem principal_add_iff_add_lt_ne_self {a} :
    Principal (· + ·) a ↔ ∀ ⦃b c⦄, b < a → c < a → b + c ≠ a :=
  ⟨fun ha b c hb hc => (ha hb hc).Ne, fun H =>
    by
    by_contra' ha
    rcases exists_lt_add_of_not_principal_add ha with ⟨b, c, hb, hc, rfl⟩
    exact (H hb hc).irrefl⟩
#align ordinal.principal_add_iff_add_lt_ne_self Ordinal.principal_add_iff_add_lt_ne_self
-/

#print Ordinal.add_omega /-
theorem add_omega {a : Ordinal} (h : a < omega) : a + omega = omega :=
  by
  rcases lt_omega.1 h with ⟨n, rfl⟩
  clear h; induction' n with n IH
  · rw [Nat.cast_zero, zero_add]
  · rwa [Nat.cast_succ, add_assoc, one_add_of_omega_le (le_refl _)]
#align ordinal.add_omega Ordinal.add_omega
-/

#print Ordinal.principal_add_omega /-
theorem principal_add_omega : Principal (· + ·) omega :=
  principal_add_iff_add_left_eq_self.2 fun a => add_omega
#align ordinal.principal_add_omega Ordinal.principal_add_omega
-/

/- warning: ordinal.add_omega_opow -> Ordinal.add_omega_opow is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} b)) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1}) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} b)) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} b))
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} b)) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} b)) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} b))
Case conversion may be inaccurate. Consider using '#align ordinal.add_omega_opow Ordinal.add_omega_opowₓ'. -/
theorem add_omega_opow {a b : Ordinal} (h : a < (omega^b)) : a + (omega^b) = (omega^b) :=
  by
  refine' le_antisymm _ (le_add_left _ _)
  revert h; refine' limit_rec_on b (fun h => _) (fun b _ h => _) fun b l IH h => _
  · rw [opow_zero, ← succ_zero, lt_succ_iff, Ordinal.le_zero] at h
    rw [h, zero_add]
  · rw [opow_succ] at h
    rcases(lt_mul_of_limit omega_is_limit).1 h with ⟨x, xo, ax⟩
    refine' le_trans (add_le_add_right (le_of_lt ax) _) _
    rw [opow_succ, ← mul_add, add_omega xo]
  · rcases(lt_opow_of_limit omega_ne_zero l).1 h with ⟨x, xb, ax⟩
    exact
      (((add_is_normal a).trans (opow_is_normal one_lt_omega)).limit_le l).2 fun y yb =>
        (add_le_add_left (opow_le_opow_right omega_pos (le_max_right _ _)) _).trans
          (le_trans
            (IH _ (max_lt xb yb) (ax.trans_le <| opow_le_opow_right omega_pos (le_max_left _ _)))
            (opow_le_opow_right omega_pos <| le_of_lt <| max_lt xb yb))
#align ordinal.add_omega_opow Ordinal.add_omega_opow

/- warning: ordinal.principal_add_omega_opow -> Ordinal.principal_add_omega_opow is a dubious translation:
lean 3 declaration is
  forall (o : Ordinal.{u1}), Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} o)
but is expected to have type
  forall (o : Ordinal.{u1}), Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2159 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2161 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2159 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2161) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} o)
Case conversion may be inaccurate. Consider using '#align ordinal.principal_add_omega_opow Ordinal.principal_add_omega_opowₓ'. -/
theorem principal_add_omega_opow (o : Ordinal) : Principal (· + ·) (omega^o) :=
  principal_add_iff_add_left_eq_self.2 fun a => add_omega_opow
#align ordinal.principal_add_omega_opow Ordinal.principal_add_omega_opow

/- warning: ordinal.principal_add_iff_zero_or_omega_opow -> Ordinal.principal_add_iff_zero_or_omega_opow is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}}, Iff (Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) o) (Or (Eq.{succ (succ u1)} Ordinal.{u1} o (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1})))) (Exists.{succ (succ u1)} Ordinal.{u1} (fun (a : Ordinal.{u1}) => Eq.{succ (succ u1)} Ordinal.{u1} o (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} a))))
but is expected to have type
  forall {o : Ordinal.{u1}}, Iff (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2194 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2196 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2194 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2196) o) (Or (Eq.{succ (succ u1)} Ordinal.{u1} o (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1}))) (Exists.{succ (succ u1)} Ordinal.{u1} (fun (a : Ordinal.{u1}) => Eq.{succ (succ u1)} Ordinal.{u1} o (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} a))))
Case conversion may be inaccurate. Consider using '#align ordinal.principal_add_iff_zero_or_omega_opow Ordinal.principal_add_iff_zero_or_omega_opowₓ'. -/
/-- The main characterization theorem for additive principal ordinals. -/
theorem principal_add_iff_zero_or_omega_opow {o : Ordinal} :
    Principal (· + ·) o ↔ o = 0 ∨ ∃ a, o = (omega^a) :=
  by
  rcases eq_or_ne o 0 with (rfl | ho)
  · simp only [principal_zero, Or.inl]
  · rw [principal_add_iff_add_left_eq_self]
    simp only [ho, false_or_iff]
    refine'
      ⟨fun H => ⟨_, ((lt_or_eq_of_le (opow_log_le_self _ ho)).resolve_left fun h => _).symm⟩,
        fun ⟨b, e⟩ => e.symm ▸ fun a => add_omega_opow⟩
    have := H _ h
    have := lt_opow_succ_log_self one_lt_omega o
    rw [opow_succ, lt_mul_of_limit omega_is_limit] at this
    rcases this with ⟨a, ao, h'⟩
    rcases lt_omega.1 ao with ⟨n, rfl⟩
    clear ao
    revert h'
    apply not_lt_of_le
    suffices e : (omega^log omega o) * ↑n + o = o
    · simpa only [e] using le_add_right ((omega^log omega o) * ↑n) o
    induction' n with n IH
    · simp only [Nat.cast_zero, MulZeroClass.mul_zero, zero_add]
    simp only [Nat.cast_succ, mul_add_one, add_assoc, this, IH]
#align ordinal.principal_add_iff_zero_or_omega_opow Ordinal.principal_add_iff_zero_or_omega_opow

/- warning: ordinal.opow_principal_add_of_principal_add -> Ordinal.opow_principal_add_of_principal_add is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}}, (Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) a) -> (forall (b : Ordinal.{u1}), Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) a b))
but is expected to have type
  forall {a : Ordinal.{u1}}, (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2473 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2475 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2473 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2475) a) -> (forall (b : Ordinal.{u1}), Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2491 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2493 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2491 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2493) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) a b))
Case conversion may be inaccurate. Consider using '#align ordinal.opow_principal_add_of_principal_add Ordinal.opow_principal_add_of_principal_addₓ'. -/
theorem opow_principal_add_of_principal_add {a} (ha : Principal (· + ·) a) (b : Ordinal) :
    Principal (· + ·) (a^b) :=
  by
  rcases principal_add_iff_zero_or_omega_opow.1 ha with (rfl | ⟨c, rfl⟩)
  · rcases eq_or_ne b 0 with (rfl | hb)
    · rw [opow_zero]
      exact principal_add_one
    · rwa [zero_opow hb]
  · rw [← opow_mul]
    exact principal_add_omega_opow _
#align ordinal.opow_principal_add_of_principal_add Ordinal.opow_principal_add_of_principal_add

/- warning: ordinal.add_absorp -> Ordinal.add_absorp is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}} {c : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} b)) -> (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} b) c) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1}) a c) c)
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}} {c : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} b)) -> (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} b) c) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) a c) c)
Case conversion may be inaccurate. Consider using '#align ordinal.add_absorp Ordinal.add_absorpₓ'. -/
theorem add_absorp {a b c : Ordinal} (h₁ : a < (omega^b)) (h₂ : (omega^b) ≤ c) : a + c = c := by
  rw [← Ordinal.add_sub_cancel_of_le h₂, ← add_assoc, add_omega_opow h₁]
#align ordinal.add_absorp Ordinal.add_absorp

/- warning: ordinal.mul_principal_add_is_principal_add -> Ordinal.mul_principal_add_is_principal_add is a dubious translation:
lean 3 declaration is
  forall (a : Ordinal.{u1}) {b : Ordinal.{u1}}, (Ne.{succ (succ u1)} Ordinal.{u1} b (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1})))) -> (Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) b) -> (Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a b))
but is expected to have type
  forall (a : Ordinal.{u1}) {b : Ordinal.{u1}}, (Ne.{succ (succ u1)} Ordinal.{u1} b (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1}))) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2704 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2706 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2704 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2706) b) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2721 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2723 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2721 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.2723) (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a b))
Case conversion may be inaccurate. Consider using '#align ordinal.mul_principal_add_is_principal_add Ordinal.mul_principal_add_is_principal_addₓ'. -/
theorem mul_principal_add_is_principal_add (a : Ordinal.{u}) {b : Ordinal.{u}} (hb₁ : b ≠ 1)
    (hb : Principal (· + ·) b) : Principal (· + ·) (a * b) :=
  by
  rcases eq_zero_or_pos a with (rfl | ha)
  · rw [MulZeroClass.zero_mul]
    exact principal_zero
  · rcases eq_zero_or_pos b with (rfl | hb₁')
    · rw [MulZeroClass.mul_zero]
      exact principal_zero
    · rw [← succ_le_iff, succ_zero] at hb₁'
      intro c d hc hd
      rw [lt_mul_of_limit (principal_add_is_limit (lt_of_le_of_ne hb₁' hb₁.symm) hb)] at *
      · rcases hc with ⟨x, hx, hx'⟩
        rcases hd with ⟨y, hy, hy'⟩
        use x + y, hb hx hy
        rw [mul_add]
        exact Left.add_lt_add hx' hy'
      assumption'
#align ordinal.mul_principal_add_is_principal_add Ordinal.mul_principal_add_is_principal_add

/-! #### Multiplicative principal ordinals -/

/- warning: ordinal.principal_mul_one -> Ordinal.principal_mul_one is a dubious translation:
lean 3 declaration is
  Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1})))
but is expected to have type
  Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3104 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3106 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3104 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3106) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1}))
Case conversion may be inaccurate. Consider using '#align ordinal.principal_mul_one Ordinal.principal_mul_oneₓ'. -/
theorem principal_mul_one : Principal (· * ·) 1 :=
  by
  rw [principal_one_iff]
  exact MulZeroClass.zero_mul _
#align ordinal.principal_mul_one Ordinal.principal_mul_one

/- warning: ordinal.principal_mul_two -> Ordinal.principal_mul_two is a dubious translation:
lean 3 declaration is
  Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (OfNat.mk.{succ u1} Ordinal.{u1} 2 (bit0.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1} (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1}))))
but is expected to have type
  Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3158 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3160 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3158 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3160) (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (instOfNat.{succ u1} Ordinal.{u1} 2 (AddMonoidWithOne.toNatCast.{succ u1} Ordinal.{u1} Ordinal.addMonoidWithOne.{u1}) (instAtLeastTwoHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))))
Case conversion may be inaccurate. Consider using '#align ordinal.principal_mul_two Ordinal.principal_mul_twoₓ'. -/
theorem principal_mul_two : Principal (· * ·) 2 := fun a b ha hb =>
  by
  have h₂ : succ (1 : Ordinal) = 2 := rfl
  rw [← h₂, lt_succ_iff] at *
  convert mul_le_mul' ha hb
  exact (mul_one 1).symm
#align ordinal.principal_mul_two Ordinal.principal_mul_two

/- warning: ordinal.principal_mul_of_le_two -> Ordinal.principal_mul_of_le_two is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}}, (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) o (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (OfNat.mk.{succ u1} Ordinal.{u1} 2 (bit0.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1} (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1}))))) -> (Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) o)
but is expected to have type
  forall {o : Ordinal.{u1}}, (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) o (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (instOfNat.{succ u1} Ordinal.{u1} 2 (AddMonoidWithOne.toNatCast.{succ u1} Ordinal.{u1} Ordinal.addMonoidWithOne.{u1}) (instAtLeastTwoHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))))) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3320 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3322 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3320 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3322) o)
Case conversion may be inaccurate. Consider using '#align ordinal.principal_mul_of_le_two Ordinal.principal_mul_of_le_twoₓ'. -/
theorem principal_mul_of_le_two {o : Ordinal} (ho : o ≤ 2) : Principal (· * ·) o :=
  by
  rcases lt_or_eq_of_le ho with (ho | rfl)
  · have h₂ : succ (1 : Ordinal) = 2 := rfl
    rw [← h₂, lt_succ_iff] at ho
    rcases lt_or_eq_of_le ho with (ho | rfl)
    · rw [lt_one_iff_zero.1 ho]
      exact principal_zero
    · exact principal_mul_one
  · exact principal_mul_two
#align ordinal.principal_mul_of_le_two Ordinal.principal_mul_of_le_two

/- warning: ordinal.principal_add_of_principal_mul -> Ordinal.principal_add_of_principal_mul is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}}, (Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) o) -> (Ne.{succ (succ u1)} Ordinal.{u1} o (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (OfNat.mk.{succ u1} Ordinal.{u1} 2 (bit0.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1} (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1}))))) -> (Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) o)
but is expected to have type
  forall {o : Ordinal.{u1}}, (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3455 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3457 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3455 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3457) o) -> (Ne.{succ (succ u1)} Ordinal.{u1} o (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (instOfNat.{succ u1} Ordinal.{u1} 2 (AddMonoidWithOne.toNatCast.{succ u1} Ordinal.{u1} Ordinal.addMonoidWithOne.{u1}) (instAtLeastTwoHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))))) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3477 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3479 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3477 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3479) o)
Case conversion may be inaccurate. Consider using '#align ordinal.principal_add_of_principal_mul Ordinal.principal_add_of_principal_mulₓ'. -/
theorem principal_add_of_principal_mul {o : Ordinal} (ho : Principal (· * ·) o) (ho₂ : o ≠ 2) :
    Principal (· + ·) o := by
  cases' lt_or_gt_of_ne ho₂ with ho₁ ho₂
  · change o < succ 1 at ho₁
    rw [lt_succ_iff] at ho₁
    exact principal_add_of_le_one ho₁
  · refine' fun a b hao hbo => lt_of_le_of_lt _ (ho (max_lt hao hbo) ho₂)
    rw [mul_two]
    exact add_le_add (le_max_left a b) (le_max_right a b)
#align ordinal.principal_add_of_principal_mul Ordinal.principal_add_of_principal_mul

/- warning: ordinal.principal_mul_is_limit -> Ordinal.principal_mul_isLimit is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (OfNat.mk.{succ u1} Ordinal.{u1} 2 (bit0.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1} (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1})))) o) -> (Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) o) -> (Ordinal.IsLimit.{u1} o)
but is expected to have type
  forall {o : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (instOfNat.{succ u1} Ordinal.{u1} 2 (AddMonoidWithOne.toNatCast.{succ u1} Ordinal.{u1} Ordinal.addMonoidWithOne.{u1}) (instAtLeastTwoHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) o) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3639 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3641 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3639 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3641) o) -> (Ordinal.IsLimit.{u1} o)
Case conversion may be inaccurate. Consider using '#align ordinal.principal_mul_is_limit Ordinal.principal_mul_isLimitₓ'. -/
theorem principal_mul_isLimit {o : Ordinal.{u}} (ho₂ : 2 < o) (ho : Principal (· * ·) o) :
    o.IsLimit :=
  principal_add_isLimit ((lt_succ 1).trans ho₂) (principal_add_of_principal_mul ho (ne_of_gt ho₂))
#align ordinal.principal_mul_is_limit Ordinal.principal_mul_isLimit

/- warning: ordinal.principal_mul_iff_mul_left_eq -> Ordinal.principal_mul_iff_mul_left_eq is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}}, Iff (Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) o) (forall (a : Ordinal.{u1}), (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a o) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a o) o))
but is expected to have type
  forall {o : Ordinal.{u1}}, Iff (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3688 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3690 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3688 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.3690) o) (forall (a : Ordinal.{u1}), (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a o) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a o) o))
Case conversion may be inaccurate. Consider using '#align ordinal.principal_mul_iff_mul_left_eq Ordinal.principal_mul_iff_mul_left_eqₓ'. -/
theorem principal_mul_iff_mul_left_eq {o : Ordinal} :
    Principal (· * ·) o ↔ ∀ a, 0 < a → a < o → a * o = o :=
  by
  refine' ⟨fun h a ha₀ hao => _, fun h a b hao hbo => _⟩
  · cases' le_or_gt o 2 with ho ho
    · convert one_mul o
      apply le_antisymm
      · have : a < succ 1 := hao.trans_le ho
        rwa [lt_succ_iff] at this
      · rwa [← succ_le_iff, succ_zero] at ha₀
    · exact op_eq_self_of_principal hao (mul_is_normal ha₀) h (principal_mul_is_limit ho h)
  · rcases eq_or_ne a 0 with (rfl | ha)
    · rwa [MulZeroClass.zero_mul]
    rw [← Ordinal.pos_iff_ne_zero] at ha
    rw [← h a ha hao]
    exact (mul_is_normal ha).StrictMono hbo
#align ordinal.principal_mul_iff_mul_left_eq Ordinal.principal_mul_iff_mul_left_eq

/- warning: ordinal.principal_mul_omega -> Ordinal.principal_mul_omega is a dubious translation:
lean 3 declaration is
  Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) Ordinal.omega.{u1}
but is expected to have type
  Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4047 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4049 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4047 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4049) Ordinal.omega.{u1}
Case conversion may be inaccurate. Consider using '#align ordinal.principal_mul_omega Ordinal.principal_mul_omegaₓ'. -/
theorem principal_mul_omega : Principal (· * ·) omega := fun a b ha hb =>
  match a, b, lt_omega.1 ha, lt_omega.1 hb with
  | _, _, ⟨m, rfl⟩, ⟨n, rfl⟩ => by
    rw [← nat_cast_mul]
    apply nat_lt_omega
#align ordinal.principal_mul_omega Ordinal.principal_mul_omega

/- warning: ordinal.mul_omega -> Ordinal.mul_omega is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a Ordinal.omega.{u1}) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a Ordinal.omega.{u1}) Ordinal.omega.{u1})
but is expected to have type
  forall {a : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a Ordinal.omega.{u1}) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a Ordinal.omega.{u1}) Ordinal.omega.{u1})
Case conversion may be inaccurate. Consider using '#align ordinal.mul_omega Ordinal.mul_omegaₓ'. -/
theorem mul_omega {a : Ordinal} (a0 : 0 < a) (ha : a < omega) : a * omega = omega :=
  principal_mul_iff_mul_left_eq.1 principal_mul_omega a a0 ha
#align ordinal.mul_omega Ordinal.mul_omega

/- warning: ordinal.mul_lt_omega_opow -> Ordinal.mul_lt_omega_opow is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}} {c : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))) c) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} c)) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) b Ordinal.omega.{u1}) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a b) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} c))
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}} {c : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})) c) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} c)) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) b Ordinal.omega.{u1}) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a b) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} c))
Case conversion may be inaccurate. Consider using '#align ordinal.mul_lt_omega_opow Ordinal.mul_lt_omega_opowₓ'. -/
theorem mul_lt_omega_opow {a b c : Ordinal} (c0 : 0 < c) (ha : a < (omega^c)) (hb : b < omega) :
    a * b < (omega^c) :=
  by
  rcases zero_or_succ_or_limit c with (rfl | ⟨c, rfl⟩ | l)
  · exact (lt_irrefl _).elim c0
  · rw [opow_succ] at ha
    rcases((mul_is_normal <| opow_pos _ omega_pos).limit_lt omega_is_limit).1 ha with ⟨n, hn, an⟩
    apply (mul_le_mul_right' (le_of_lt an) _).trans_lt
    rw [opow_succ, mul_assoc, mul_lt_mul_iff_left (opow_pos _ omega_pos)]
    exact principal_mul_omega hn hb
  · rcases((opow_is_normal one_lt_omega).limit_lt l).1 ha with ⟨x, hx, ax⟩
    refine' (mul_le_mul' (le_of_lt ax) (le_of_lt hb)).trans_lt _
    rw [← opow_succ, opow_lt_opow_iff_right one_lt_omega]
    exact l.2 _ hx
#align ordinal.mul_lt_omega_opow Ordinal.mul_lt_omega_opow

/- warning: ordinal.mul_omega_opow_opow -> Ordinal.mul_omega_opow_opow is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} b))) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} b))) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} b)))
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} b))) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} b))) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} b)))
Case conversion may be inaccurate. Consider using '#align ordinal.mul_omega_opow_opow Ordinal.mul_omega_opow_opowₓ'. -/
theorem mul_omega_opow_opow {a b : Ordinal} (a0 : 0 < a) (h : a < (omega^omega^b)) :
    a * (omega^omega^b) = (omega^omega^b) :=
  by
  by_cases b0 : b = 0;
  · rw [b0, opow_zero, opow_one] at h⊢
    exact mul_omega a0 h
  refine'
    le_antisymm _
      (by simpa only [one_mul] using mul_le_mul_right' (one_le_iff_pos.2 a0) (omega^omega^b))
  rcases(lt_opow_of_limit omega_ne_zero (opow_is_limit_left omega_is_limit b0)).1 h with ⟨x, xb, ax⟩
  apply (mul_le_mul_right' (le_of_lt ax) _).trans
  rw [← opow_add, add_omega_opow xb]
#align ordinal.mul_omega_opow_opow Ordinal.mul_omega_opow_opow

/- warning: ordinal.principal_mul_omega_opow_opow -> Ordinal.principal_mul_omega_opow_opow is a dubious translation:
lean 3 declaration is
  forall (o : Ordinal.{u1}), Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} o))
but is expected to have type
  forall (o : Ordinal.{u1}), Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4597 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4599 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4597 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4599) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} o))
Case conversion may be inaccurate. Consider using '#align ordinal.principal_mul_omega_opow_opow Ordinal.principal_mul_omega_opow_opowₓ'. -/
theorem principal_mul_omega_opow_opow (o : Ordinal) : Principal (· * ·) (omega^omega^o) :=
  principal_mul_iff_mul_left_eq.2 fun a => mul_omega_opow_opow
#align ordinal.principal_mul_omega_opow_opow Ordinal.principal_mul_omega_opow_opow

/- warning: ordinal.principal_add_of_principal_mul_opow -> Ordinal.principal_add_of_principal_mul_opow is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}} {b : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1}))) b) -> (Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) b o)) -> (Ordinal.Principal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1})) o)
but is expected to have type
  forall {o : Ordinal.{u1}} {b : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1})) b) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4637 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4639 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4637 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4639) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) b o)) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4660 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4662 : Ordinal.{u1}) => HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4660 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4662) o)
Case conversion may be inaccurate. Consider using '#align ordinal.principal_add_of_principal_mul_opow Ordinal.principal_add_of_principal_mul_opowₓ'. -/
theorem principal_add_of_principal_mul_opow {o b : Ordinal} (hb : 1 < b)
    (ho : Principal (· * ·) (b^o)) : Principal (· + ·) o := fun x y hx hy =>
  by
  have := ho ((opow_lt_opow_iff_right hb).2 hx) ((opow_lt_opow_iff_right hb).2 hy)
  rwa [← opow_add, opow_lt_opow_iff_right hb] at this
#align ordinal.principal_add_of_principal_mul_opow Ordinal.principal_add_of_principal_mul_opow

/- warning: ordinal.principal_mul_iff_le_two_or_omega_opow_opow -> Ordinal.principal_mul_iff_le_two_or_omega_opow_opow is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}}, Iff (Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) o) (Or (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) o (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (OfNat.mk.{succ u1} Ordinal.{u1} 2 (bit0.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1} (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1}))))) (Exists.{succ (succ u1)} Ordinal.{u1} (fun (a : Ordinal.{u1}) => Eq.{succ (succ u1)} Ordinal.{u1} o (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) Ordinal.omega.{u1} a)))))
but is expected to have type
  forall {o : Ordinal.{u1}}, Iff (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4770 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4772 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4770 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.4772) o) (Or (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) o (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (instOfNat.{succ u1} Ordinal.{u1} 2 (AddMonoidWithOne.toNatCast.{succ u1} Ordinal.{u1} Ordinal.addMonoidWithOne.{u1}) (instAtLeastTwoHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))))) (Exists.{succ (succ u1)} Ordinal.{u1} (fun (a : Ordinal.{u1}) => Eq.{succ (succ u1)} Ordinal.{u1} o (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) Ordinal.omega.{u1} a)))))
Case conversion may be inaccurate. Consider using '#align ordinal.principal_mul_iff_le_two_or_omega_opow_opow Ordinal.principal_mul_iff_le_two_or_omega_opow_opowₓ'. -/
/-- The main characterization theorem for multiplicative principal ordinals. -/
theorem principal_mul_iff_le_two_or_omega_opow_opow {o : Ordinal} :
    Principal (· * ·) o ↔ o ≤ 2 ∨ ∃ a, o = (omega^omega^a) :=
  by
  refine' ⟨fun ho => _, _⟩
  · cases' le_or_lt o 2 with ho₂ ho₂
    · exact Or.inl ho₂
    rcases principal_add_iff_zero_or_omega_opow.1 (principal_add_of_principal_mul ho ho₂.ne') with
      (rfl | ⟨a, rfl⟩)
    · exact (Ordinal.not_lt_zero 2 ho₂).elim
    rcases principal_add_iff_zero_or_omega_opow.1
        (principal_add_of_principal_mul_opow one_lt_omega ho) with
      (rfl | ⟨b, rfl⟩)
    · rw [opow_zero] at ho₂
      exact ((lt_succ 1).not_le ho₂.le).elim
    exact Or.inr ⟨b, rfl⟩
  · rintro (ho₂ | ⟨a, rfl⟩)
    · exact principal_mul_of_le_two ho₂
    · exact principal_mul_omega_opow_opow a
#align ordinal.principal_mul_iff_le_two_or_omega_opow_opow Ordinal.principal_mul_iff_le_two_or_omega_opow_opow

/- warning: ordinal.mul_omega_dvd -> Ordinal.mul_omega_dvd is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a Ordinal.omega.{u1}) -> (forall {b : Ordinal.{u1}}, (Dvd.Dvd.{succ u1} Ordinal.{u1} (semigroupDvd.{succ u1} Ordinal.{u1} (SemigroupWithZero.toSemigroup.{succ u1} Ordinal.{u1} (MonoidWithZero.toSemigroupWithZero.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))) Ordinal.omega.{u1} b) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a b) b))
but is expected to have type
  forall {a : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a Ordinal.omega.{u1}) -> (forall {b : Ordinal.{u1}}, (Dvd.dvd.{succ u1} Ordinal.{u1} (semigroupDvd.{succ u1} Ordinal.{u1} (SemigroupWithZero.toSemigroup.{succ u1} Ordinal.{u1} (MonoidWithZero.toSemigroupWithZero.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))) Ordinal.omega.{u1} b) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a b) b))
Case conversion may be inaccurate. Consider using '#align ordinal.mul_omega_dvd Ordinal.mul_omega_dvdₓ'. -/
theorem mul_omega_dvd {a : Ordinal} (a0 : 0 < a) (ha : a < omega) : ∀ {b}, omega ∣ b → a * b = b
  | _, ⟨b, rfl⟩ => by rw [← mul_assoc, mul_omega a0 ha]
#align ordinal.mul_omega_dvd Ordinal.mul_omega_dvd

/- warning: ordinal.mul_eq_opow_log_succ -> Ordinal.mul_eq_opow_log_succ is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, (Ne.{succ (succ u1)} Ordinal.{u1} a (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1})))) -> (Ordinal.Principal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1}))))) b) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (OfNat.mk.{succ u1} Ordinal.{u1} 2 (bit0.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1} (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1})))) b) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toHasMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a b) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) b (Order.succ.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1}) Ordinal.succOrder.{u1} (Ordinal.log.{u1} b a))))
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, (Ne.{succ (succ u1)} Ordinal.{u1} a (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1}))) -> (Ordinal.Principal.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.5010 : Ordinal.{u1}) (x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.5012 : Ordinal.{u1}) => HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.5010 x._@.Mathlib.SetTheory.Ordinal.Principal._hyg.5012) b) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 2 (instOfNat.{succ u1} Ordinal.{u1} 2 (AddMonoidWithOne.toNatCast.{succ u1} Ordinal.{u1} Ordinal.addMonoidWithOne.{u1}) (instAtLeastTwoHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) b) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHMul.{succ u1} Ordinal.{u1} (MulZeroClass.toMul.{succ u1} Ordinal.{u1} (MulZeroOneClass.toMulZeroClass.{succ u1} Ordinal.{u1} (MonoidWithZero.toMulZeroOneClass.{succ u1} Ordinal.{u1} Ordinal.monoidWithZero.{u1})))) a b) (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) b (Order.succ.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1}) Ordinal.succOrder.{u1} (Ordinal.log.{u1} b a))))
Case conversion may be inaccurate. Consider using '#align ordinal.mul_eq_opow_log_succ Ordinal.mul_eq_opow_log_succₓ'. -/
theorem mul_eq_opow_log_succ {a b : Ordinal.{u}} (ha : a ≠ 0) (hb : Principal (· * ·) b)
    (hb₂ : 2 < b) : a * b = (b^succ (log b a)) :=
  by
  apply le_antisymm
  · have hbl := principal_mul_is_limit hb₂ hb
    rw [← IsNormal.bsup_eq.{u, u} (mul_is_normal (Ordinal.pos_iff_ne_zero.2 ha)) hbl, bsup_le_iff]
    intro c hcb
    have hb₁ : 1 < b := (lt_succ 1).trans hb₂
    have hbo₀ : (b^b.log a) ≠ 0 := Ordinal.pos_iff_ne_zero.1 (opow_pos _ (zero_lt_one.trans hb₁))
    apply le_trans (mul_le_mul_right' (le_of_lt (lt_mul_succ_div a hbo₀)) c)
    rw [mul_assoc, opow_succ]
    refine' mul_le_mul_left' (le_of_lt (hb (hbl.2 _ _) hcb)) _
    rw [div_lt hbo₀, ← opow_succ]
    exact lt_opow_succ_log_self hb₁ _
  · rw [opow_succ]
    exact mul_le_mul_right' (opow_log_le_self b ha) b
#align ordinal.mul_eq_opow_log_succ Ordinal.mul_eq_opow_log_succ

/-! #### Exponential principal ordinals -/

/- warning: ordinal.principal_opow_omega -> Ordinal.principal_opow_omega is a dubious translation:
lean 3 declaration is
  Ordinal.Principal.{u1} (fun (_x : Ordinal.{u1}) (_y : Ordinal.{u1}) => HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) _x _y) Ordinal.omega.{u1}
but is expected to have type
  Ordinal.Principal.{u1} (fun (_x : Ordinal.{u1}) (_y : Ordinal.{u1}) => HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) _x _y) Ordinal.omega.{u1}
Case conversion may be inaccurate. Consider using '#align ordinal.principal_opow_omega Ordinal.principal_opow_omegaₓ'. -/
theorem principal_opow_omega : Principal (·^·) omega := fun a b ha hb =>
  match a, b, lt_omega.1 ha, lt_omega.1 hb with
  | _, _, ⟨m, rfl⟩, ⟨n, rfl⟩ => by
    simp_rw [← nat_cast_opow]
    apply nat_lt_omega
#align ordinal.principal_opow_omega Ordinal.principal_opow_omega

/- warning: ordinal.opow_omega -> Ordinal.opow_omega is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1}))) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a Ordinal.omega.{u1}) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.hasPow.{u1}) a Ordinal.omega.{u1}) Ordinal.omega.{u1})
but is expected to have type
  forall {a : Ordinal.{u1}}, (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1})) a) -> (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) a Ordinal.omega.{u1}) -> (Eq.{succ (succ u1)} Ordinal.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHPow.{succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.pow.{u1}) a Ordinal.omega.{u1}) Ordinal.omega.{u1})
Case conversion may be inaccurate. Consider using '#align ordinal.opow_omega Ordinal.opow_omegaₓ'. -/
theorem opow_omega {a : Ordinal} (a1 : 1 < a) (h : a < omega) : (a^omega) = omega :=
  le_antisymm
    ((opow_le_of_limit (one_le_iff_ne_zero.1 <| le_of_lt a1) omega_isLimit).2 fun b hb =>
      (principal_opow_omega h hb).le)
    (right_le_opow _ a1)
#align ordinal.opow_omega Ordinal.opow_omega

end Ordinal


import hp.tactic.hp_interactive

example {P Q: Prop} : Π (q : Q), (Q → P) → P :=
begin [hp]
  intros,
  apply H₀,
end

example {Q : Prop} {P : nat → Prop} : Π (h₀ : Q) (h₁ : Q → P 0) (h₂ : (∀ x (h : P x), P (x+1))), P 2:=
begin [hp]
  unroll,
  apply H₁, -- [todo] reads clunky. I need to improve the statement writing heuristics.
  apply H₁,
  apply H₀,
end

-- example : ∀ x:nat, (λ z, z = z) x :=
-- begin [hp]
--   equate
-- end

/- Target:

Let Q, P and R be propositions. Assume P → R, Q → R and P ∨ Q.
Since P ∨ Q, we have either P or Q.

In the case P: Since P → R we are done.

In the case Q: Since Q → R we are done.

 -/

example {Q P R : Prop} : (P → R) → (Q → R) → P ∨ Q → R :=
begin [hp]
  unroll, -- [todo] cases button on H₁
  -- idea: something should hint that applying is not the right approach here?
  cases H₁,
  apply H,
  apply H,

end

-- -- -- set_option pp.all true
-- example : ∀ x y : nat, ∃ z, y = z :=
-- begin [hp]
--   -- [todo] rogue full stop at beginning
--   unroll,

-- end

-- example {P Q : Prop} : P → Q → Q ∧ P :=
-- begin [hp]

-- end

-- example : Π {α : Type} [group α] (x y : α), x = y :=
-- begin [hp]
--   (hp.down 0 0 *> hp.intros),
--   (hp.trace_writeup),

-- end


import topology.basic
import algebra.module.basic
import algebra.module.submodule

import analysis.normed_space.basic

-- Montrer que tout fermé peut s'écrire comme intersection d'une suite décroissante
-- d'ouverts.

theorem exo {R E: Type*} [normed_field R] [normed_group E] [normed_space R E] :
  forall A: set E, is_closed A -> (exists (B: nat -> set E),
       (forall i, is_open (B i)) -- open
    /\ (forall (n m: nat), n < m -> set.subset (B n) (B m) ) -- decreasing
    /\ (forall x: E, (set.mem x A) <-> (forall i, set.mem x (B i)))) -- equality of intersection.
:= sorry


universe u

#check @eq.refl.{u}
#check @eq.symm.{u}
#check @eq.trans
-- If '@' is used but no universe is specified it uses a default one it seems

namespace prev
variables (α : Type u) (a b c d : α)
variables (hab : a = b) (hcb : c = b) (hcd : c = d)

example : a = d := (hab.trans hcb.symm).trans hcd
end prev

namespace refl
variables (α β : Type u)

example (f : α → β) (a : α) : (λx, f x) a = f a := eq.refl (f a)
example (f : α → β) (a : α) : (λx, f x) a = f a := eq.refl _
example (f : α → β) (a : α) : (λx, f x) a = f a := rfl
example (a : α) (b : α) : (a, b).1 = a := eq.refl a
example (a : α) (b : α) : (a, b).1 = a := eq.refl _
example (a : α) (b : α) : (a, b).1 = a := rfl
example : 2 + 3 = 5 := eq.refl 5
example : 2 + 3 = 5 := eq.refl _
example : 2 + 3 = 5 := rfl
end refl

namespace reflequiv

example (α : Type u) (a b : α) (p : α → Prop) (h1 :a = b) (h2 : p a) : p b :=
eq.subst h1 h2

example (α : Type u) (a b : α) (p : α → Prop)
  (h1 : a = b) (h2 : p a) : p b :=
h1 ▸ h2
end reflequiv

namespace cong

variable α : Type
variables a b : α
variables f g : α → ℕ
variable h₁ : a = b
variable h₂ : f = g
example : f a = f b := congr_arg _ h₁
example : f a = g a := congr_fun h₂ _
example : f a = g b := congr h₂ h₁
end cong

namespace ints

variables x y z : ℤ
example (x y z : ℕ) : x * (y + z) = x * y + x * z := mul_add x y z
example (x y z : ℕ) : (x + y) * z = x * z + y * z := add_mul x y z
example (x y z : ℕ) : x + y + z = x + (y + z) := add_assoc x y z

example (x y : ℕ) : (x + y) * (x + y) = x * x + y * x + x * y + y * y :=
have h1 : (x + y) * (x + y) = (x + y) * x + (x + y) * y,
  from left_distrib (x + y) x y,
have h2 : (x + y) * (x + y) = x * x + y * x + (x * y + y * y),
  from (right_distrib x y y) ▸ (right_distrib x y x) ▸ h1,
(add_assoc (x * x + y * x) (x * y) (y * y)).symm ▸ h2
-- or:
-- h2.trans (add_assoc (x * x + y * x) (x * y) (y * y)).symm

end ints


lemma and_trans (P Q R : Prop) : P ∧ Q → Q ∧ R → P ∧ R :=
begin
    intros pq qr,
    cases pq with p q,
    cases qr with q r,
    split,
    {
        exact p,
    },
    {
        exact r,
    },
end


import data.set.basic

import kb_defs

namespace xena -- hide

variable X : Type -- we will think of X as a set here

-- begin hide
/- The first level, originally written by KMB. -/
-- end hide

/-
# Chapter 1 : Sets

## Level 1 : Introduction to sets.

This chapter assumes you are familiar with the following tactics:
`rw`, `intro`, `apply`, `exact`, `cases`, `split`, `left`, `right` and `exfalso`.<br>

(TODO (kmb) : check this list is exhaustive)

If you are not, try playing Function World and Proposition World of the Natural Number Game.

## Sets in Lean

In this world, there will be an ambient "big" set `X` (actually `X` will be a type),
and we will consider subsets of `X`. The type of subsets of `X` is called `set X`.
So if you see `A : set X` it means that `A` is a subset of `X`.

## subsets (⊆) and `subset_iff`

If `A B : set X` (i.e. `A` and `B` are subsets of `X`), then `A ⊆ B` is a
Proposition, i.e. a true/false statement. Lean knows the following fact:

```
subset_iff : A ⊆ B ↔ ∀ x : X, x ∈ A → x ∈ B
```

Let's see if you can prove `A ⊆ A` by rewriting `subset_iff`.
-/

/- Axiom : subset_iff
A ⊆ B ↔ ∀ x : X, x ∈ A → x ∈ B
-/

/- Hint : Hint
To make progress with a goal of form `∀ a : X, ...`, introduce a term of type `X` by using a familiar tactic.

In this example, using

`intro a,`

will introduce an arbitrary term of type X.

Note that this is the tactic we use to assume a hypothesis (when proving an implication), or to choose an arbitrary element of some domain (when defining a function).

Use the same tactic to introduce an appropriately named hypothesis for an implication, and close the goal with the `exact` tactic.
-/

/-
If you get stuck, you can click on the hints for more details!
-/

/- Lemma
If $A$ is a set of elements of type X, then $A \subseteq A$.
-/
lemma subset.refl (A : set X) : A ⊆ A :=
begin
  rw subset_iff,
  intros x h,
  exact h
end

end xena --hide


import group_theory.order_of_element

-- Order of a Subgroup

theorem t071 {α : Type*} [group α] [fintype α] [decidable_eq α] (s : set α) [is_subgroup s] [fintype s] :
  fintype.card s ∣ fintype.card α
:= card_subgroup_dvd_card s


def all (p : Nat → Prop) : Prop := ∀x, p x
example {p : Nat → Prop} (h : all p) : p 0 := h 0


import tactic
import data.real.irrational

def rational (x : ℝ) :=
  ∃ (a b : ℤ), x = a / b

lemma part_a : irrational (real.sqrt 2 + real.sqrt (3/2)) := begin
  sorry
end

lemma part_b : irrational (1 + real.sqrt 2 + real.sqrt (3/2)) := begin
  sorry
end

lemma part_c : rational (2 * real.sqrt 18 - 3 * real.sqrt 8 + real.sqrt 4) := begin
  sorry
end

lemma part_d : irrational (real.sqrt 2 + real.sqrt 3 + real.sqrt 5) :=
begin
  sorry
end

lemma part_e : rational (real.sqrt 2 + real.sqrt 3 - real.sqrt (5 + 2 * real.sqrt 6)) :=
begin
  sorry
end


import SciLean.Core.Functions

namespace SciLean

variable {α β γ : Type}
variable {X Y Z : Type} [Hilbert X] [Hilbert Y] [Hilbert Z]

example : IsLin (SemiInner.semiInner : X → X → _) := by infer_instance done
example Ω : IsLin (λ x y : X => ⟪x, y⟫[Ω]) := by infer_instance done
example (x : X) Ω : IsLin (λ y : X => ⟪x, y⟫[Ω]) := by infer_instance done

-- example {X Y R D eval} [Vec R] [FinVec X] [SemiInner Y R D eval] [Vec Y]
--   : SemiInner (X ⊸ Y) R D eval := by infer_instance

-- example {X Y R D eval} [Vec R] [FinVec X] [SemiHilbert Y R D eval]
--   : SemiHilbert (X ⊸ Y) R D eval := by infer_instance

-- example {X Y} [FinVec X] [Hilbert Y] : Hilbert (X ⊸ Y) := by infer_instance
-- example {Y}                  [Hilbert Y] : Hilbert (ℝ ⊸ Y) := by infer_instance

-- example {X} [FinVec X] : SemiInner (X ⊸ ℝ) ℝ Unit (λ r _ => r) := by infer_instance
-- example {X} [FinVec X] : Hilbert    (X ⊸ ℝ)                     := by infer_instance

-- This was a problem at some point
section mul_test
  variable {X : Type} [Hilbert X] (x y : X) (r s : ℝ)
  #check r * x
end mul_test


/-
Copyright (c) 2018 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Simon Hudon
-/
import data.pfunctor.multivariate.basic

/-!
# Multivariate quotients of polynomial functors.

Basic definition of multivariate QPF. QPFs form a compositional framework
for defining inductive and coinductive types, their quotients and nesting.

The idea is based on building ever larger functors. For instance, we can define
a list using a shape functor:

```lean
inductive list_shape (a b : Type)
| nil : list_shape
| cons : a -> b -> list_shape
```

This shape can itself be decomposed as a sum of product which are themselves
QPFs. It follows that the shape is a QPF and we can take its fixed point
and create the list itself:

```lean
def list (a : Type) := fix list_shape a -- not the actual notation
```

We can continue and define the quotient on permutation of lists and create
the multiset type:

```lean
def multiset (a : Type) := qpf.quot list.perm list a -- not the actual notion
```

And `multiset` is also a QPF. We can then create a novel data type (for Lean):

```lean
inductive tree (a : Type)
| node : a -> multiset tree -> tree
```

An unordered tree. This is currently not supported by Lean because it nests
an inductive type inside of a quotient. We can go further and define
unordered, possibly infinite trees:

```lean
coinductive tree' (a : Type)
| node : a -> multiset tree' -> tree'
```

by using the `cofix` construct. Those options can all be mixed and
matched because they preserve the properties of QPF. The latter example,
`tree'`, combines fixed point, co-fixed point and quotients.

## Related modules

 * constructions
   * fix
   * cofix
   * quot
   * comp
   * sigma / pi
   * prj
   * const

each proves that some operations on functors preserves the QPF structure

##reference

 * [Jeremy Avigad, Mario M. Carneiro and Simon Hudon, *Data Types as Quotients of Polynomial Functors*][avigad-carneiro-hudon2019]
-/

universe u

open_locale mvfunctor

/--
Multivariate quotients of polynomial functors.
-/
class mvqpf {n : ℕ} (F : typevec.{u} n → Type*) [mvfunctor F] :=
(P         : mvpfunctor.{u} n)
(abs       : Π {α}, P.obj α → F α)
(repr      : Π {α}, F α → P.obj α)
(abs_repr  : ∀ {α} (x : F α), abs (repr x) = x)
(abs_map   : ∀ {α β} (f : α ⟹ β) (p : P.obj α), abs (f <$$> p) = f <$$> abs p)

namespace mvqpf
variables {n : ℕ} {F : typevec.{u} n → Type*} [mvfunctor F] [q : mvqpf F]
include q
open mvfunctor (liftp liftr)

/-!
### Show that every mvqpf is a lawful mvfunctor.
-/

protected theorem id_map {α : typevec n} (x : F α) : typevec.id <$$> x = x :=
by { rw ←abs_repr x, cases repr x with a f, rw [←abs_map], reflexivity }

@[simp] theorem comp_map {α β γ : typevec n} (f : α ⟹ β) (g : β ⟹ γ) (x : F α) :
  (g ⊚ f) <$$> x = g <$$> f <$$> x :=
by { rw ←abs_repr x, cases repr x with a f, rw [←abs_map, ←abs_map, ←abs_map], reflexivity }

@[priority 100]
instance is_lawful_mvfunctor : is_lawful_mvfunctor F :=
{ id_map := @mvqpf.id_map n F _ _,
  comp_map := @comp_map n F _ _ }

/- Lifting predicates and relations -/

theorem liftp_iff {α : typevec n} (p : Π ⦃i⦄, α i → Prop) (x : F α) :
  liftp p x ↔ ∃ a f, x = abs ⟨a, f⟩ ∧ ∀ i j, p (f i j) :=
begin
  split,
  { rintros ⟨y, hy⟩, cases h : repr y with a f,
    use [a, λ i j, (f i j).val], split,
    { rw [←hy, ←abs_repr y, h, ←abs_map], reflexivity },
    intros i j, apply (f i j).property },
  rintros ⟨a, f, h₀, h₁⟩, dsimp at *,
  use abs (⟨a, λ i j, ⟨f i j, h₁ i j⟩⟩),
  rw [←abs_map, h₀], reflexivity
end

theorem liftr_iff {α : typevec n} (r : Π ⦃i⦄, α i → α i → Prop) (x y : F α) :
  liftr r x y ↔ ∃ a f₀ f₁, x = abs ⟨a, f₀⟩ ∧ y = abs ⟨a, f₁⟩ ∧ ∀ i j, r (f₀ i j) (f₁ i j) :=
begin
  split,
  { rintros ⟨u, xeq, yeq⟩, cases h : repr u with a f,
    use [a, λ i j, (f i j).val.fst, λ i j, (f i j).val.snd],
    split, { rw [←xeq, ←abs_repr u, h, ←abs_map], refl },
    split, { rw [←yeq, ←abs_repr u, h, ←abs_map], refl },
    intros i j, exact (f i j).property },
  rintros ⟨a, f₀, f₁, xeq, yeq, h⟩,
  use abs ⟨a, λ i j, ⟨(f₀ i j, f₁ i j), h i j⟩⟩,
  dsimp, split,
  { rw [xeq, ←abs_map], refl },
  rw [yeq, ←abs_map], refl
end

open set
open mvfunctor

theorem mem_supp {α : typevec n} (x : F α) (i) (u : α i) :
  u ∈ supp x i ↔ ∀ a f, abs ⟨a, f⟩ = x → u ∈ f i '' univ :=
begin
  rw [supp], dsimp, split,
  { intros h a f haf,
    have : liftp (λ i u, u ∈ f i '' univ) x,
    { rw liftp_iff, refine ⟨a, f, haf.symm, _⟩,
      intros i u, exact mem_image_of_mem _ (mem_univ _) },
    exact h this },
  intros h p, rw liftp_iff,
  rintros ⟨a, f, xeq, h'⟩,
  rcases h a f xeq.symm with ⟨i, _, hi⟩,
  rw ←hi, apply h'
end

theorem supp_eq {α : typevec n} {i} (x : F α) :
  supp x i = { u | ∀ a f, abs ⟨a, f⟩ = x → u ∈ f i '' univ } :=
by ext; apply mem_supp

theorem has_good_supp_iff {α : typevec n} (x : F α) :
  (∀ p, liftp p x ↔ ∀ i (u ∈ supp x i), p i u) ↔
    ∃ a f, abs ⟨a, f⟩ = x ∧ ∀ i a' f', abs ⟨a', f'⟩ = x → f i '' univ ⊆ f' i '' univ :=
begin
  split,
  { intros h,
    have : liftp (supp x) x, by { rw h, introv, exact id, },
    rw liftp_iff at this, rcases this with ⟨a, f, xeq, h'⟩,
    refine ⟨a, f, xeq.symm, _⟩,
    intros a' f' h'',
    rintros hu u ⟨j, h₂, hfi⟩,
    have hh : u ∈ supp x a', by rw ←hfi; apply h',
    refine (mem_supp x _ u).mp hh _ _ hu, },
  rintros ⟨a, f, xeq, h⟩ p, rw liftp_iff, split,
  { rintros ⟨a', f', xeq', h'⟩ i u usuppx,
    rcases (mem_supp x _ u).mp @usuppx a' f' xeq'.symm with ⟨i, _, f'ieq⟩,
    rw ←f'ieq, apply h' },
  intro h',
  refine ⟨a, f, xeq.symm, _⟩, intros j y,
  apply h', rw mem_supp,
  intros a' f' xeq',
  apply h _ a' f' xeq',
  apply mem_image_of_mem _ (mem_univ _)
end

variable (q)

/-- A qpf is said to be uniform if every polynomial functor
representing a single value all have the same range. -/
def is_uniform : Prop := ∀ ⦃α : typevec n⦄ (a a' : q.P.A)
    (f : q.P.B a ⟹ α) (f' : q.P.B a' ⟹ α),
  abs ⟨a, f⟩ = abs ⟨a', f'⟩ → ∀ i, f i '' univ = f' i '' univ

/-- does `abs` preserve `liftp`? -/
def liftp_preservation : Prop :=
∀ ⦃α : typevec n⦄ (p : Π ⦃i⦄, α i → Prop) (x : q.P.obj α), liftp p (abs x) ↔ liftp p x

/-- does `abs` preserve `supp`? -/
def supp_preservation : Prop :=
∀ ⦃α⦄ (x : q.P.obj α), supp (abs x) = supp x

variable [q]

theorem supp_eq_of_is_uniform (h : q.is_uniform) {α : typevec n} (a : q.P.A) (f : q.P.B a ⟹ α) :
  ∀ i, supp (abs ⟨a, f⟩) i = f i '' univ :=
begin
  intro, ext u, rw [mem_supp], split,
  { intro h', apply h' _ _ rfl },
  intros h' a' f' e,
  rw [←h _ _ _ _ e.symm], apply h'
end

theorem liftp_iff_of_is_uniform (h : q.is_uniform) {α : typevec n} (x : F α) (p : Π i, α i → Prop) :
  liftp p x ↔ ∀ i (u ∈ supp x i), p i u :=
begin
  rw [liftp_iff, ←abs_repr x],
  cases repr x with a f,  split,
  { rintros ⟨a', f', abseq, hf⟩ u,
    rw [supp_eq_of_is_uniform h, h _ _ _ _ abseq],
    rintros b ⟨i, _, hi⟩, rw ←hi, apply hf },
  intro h',
  refine ⟨a, f, rfl, λ _ i, h' _ _ _⟩,
  rw supp_eq_of_is_uniform h,
  exact ⟨i, mem_univ i, rfl⟩
end

theorem supp_map (h : q.is_uniform) {α β : typevec n} (g : α ⟹ β) (x : F α) (i) :
  supp (g <$$> x) i = g i '' supp x i :=
begin
  rw ←abs_repr x, cases repr x with a f, rw [←abs_map, mvpfunctor.map_eq],
  rw [supp_eq_of_is_uniform h, supp_eq_of_is_uniform h, ← image_comp],
  refl,
end

theorem supp_preservation_iff_uniform :
  q.supp_preservation ↔ q.is_uniform :=
begin
  split,
  { intros h α a a' f f' h' i,
    rw [← mvpfunctor.supp_eq,← mvpfunctor.supp_eq,← h,h',h] },
  { rintros h α ⟨a,f⟩, ext, rwa [supp_eq_of_is_uniform,mvpfunctor.supp_eq], }
end

theorem liftp_preservation_iff_uniform :
  q.liftp_preservation ↔ q.is_uniform :=
by rw [← supp_preservation_iff_liftp_preservation, supp_preservation_iff_uniform]

end mvqpf


import linear_algebra.nonsingular_inverse
import linear_algebra.matrix
import linear_algebra.finite_dimensional
import basic_definitions.equiv
import ring_theory.ideals
import Tools.tools
import ring_theory.ideal_operations
set_option pp.generalized_field_notation false
universes u v
variables variables {X : Type u} [fintype X] [decidable_eq X] {R : Type v}[field R]
open matrix linear_map
open_locale matrix
lemma matrix.smul_zero (r : R) : r • (0 : matrix X X R) = 0 :=
begin
    ext, rw smul_val, rw zero_val, rw mul_zero,
end
lemma matrix.zero_smul ( M : matrix X X R) : (0  : R) • M = 0 :=
begin
    ext,rw smul_val, rw zero_mul, exact rfl,
end
/--
Change to non empty or something like that
-/
lemma terere  (M : matrix X X R) (N : matrix X X R) (x : X) : mul_vec M (λ i, N i x) = λ i, (M ⬝ N) i x := rfl
lemma kernel_nonempty (M : matrix X X R) (x : X) : det M = 0 → ∃ v : X → R, mul_vec  M v  = 0 :=
begin
    intros,
    let adj := adjugate M,
    use λ i,adj i x,
    let f := mul_adjugate M, rw a at f, erw matrix.zero_smul 1 at f,
    rw terere,funext,  rw f, exact rfl, assumption,
end
/--
    A revoir un peu, ce n'est pas bon du toiut j'ai oublié le vecteur nul !
-/
lemma kernel_nonempty_lin (φ : (X → R) →ₗ[R] (X → R))(x : X) :det (linear_map.to_matrix φ)  = 0 →  (∃ v : X → R, φ v = 0) :=
begin
    intros, rcases kernel_nonempty (linear_map.to_matrix φ) x a with ⟨ ζ,hyp ⟩,use ζ,
    have : φ = matrix.to_lin (linear_map.to_matrix φ),
        rw to_matrix_to_lin,
        rw this,
        rw to_lin_apply (to_matrix φ ) ζ ,
        rw hyp,
end
variables {Y : Type u} [fintype Y] [decidable_eq Y]
def is_injective (M : matrix X Y R) := (∀ v : Y → R, mul_vec M v = 0 → v = 0 )

lemma is_injective_to_lin_ker (M : matrix X X R) (hyp : is_injective M) : ker (to_lin M) = ⊥ :=
begin
    rw eq_bot_iff, rw submodule.le_def',
     intros, rw submodule.mem_bot,
    rw mem_ker at H, rw to_lin_apply at H, exact hyp x H,
end
#check X
/-!
    Le theorem fondamentale du rang !
-/
open equiv_morphism
open_locale classical
theorem exist_ker_not_trivial (φ : (X→ R) →ₗ[R] (X → R)) : det  (linear_map.to_matrix φ) = 0 → (∃ v : X → R, (v ≠  0) ∧  (φ v = 0)) := begin
    intros, by_contradiction,
    push_neg at a_1,
    have : ker φ = ⊥,
        rw eq_bot_iff,
        rw submodule.le_def',
        intros,
        erw submodule.mem_bot,specialize a_1 x,
        cases a_1, exact a_1, rw mem_ker at *, trivial ,
        have range : range φ  = ⊤,
             apply linear_map.ker_eq_bot_iff_range_eq_top.mp, exact this,
        by apply_instance,
        let gk := equiv_morphism.ker_im_equiv'' φ ⟨this, range ⟩,
        have fact1 : φ = linear_equiv.to_linear_map gk,
            rw equiv_morphism.ker_im_equiv_ext'',
        let φ' := linear_equiv.to_linear_map gk.symm,
        have : φ ⊚ φ'  =  linear_map.id,
          rw fact1, rw linear_equiv_compo_id,
        have Fact2:  (linear_map.to_matrix φ) ⬝ (linear_map.to_matrix φ') = 1,
            rw ← to_matrix_mul, rw this, erw to_matrix_one,
        have : (det (linear_map.to_matrix φ)) * (det (linear_map.to_matrix φ'))=1,
            rw ← det_mul,
            rw Fact2, rw det_one,
        erw [a, zero_mul] at this,
        exact zero_ne_one this,
end