/-
Copyright (c) 2020 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.fintype.card
import Mathlib.data.finset.sort
import Mathlib.tactic.omega.default
import Mathlib.algebra.big_operators.order
import Mathlib.PostPort

universes l u_1

namespace Mathlib

/-!
# Compositions

A composition of a natural number `n` is a decomposition `n = i₀ + ... + i_{k-1}` of `n` into a sum
of positive integers. Combinatorially, it corresponds to a decomposition of `{0, ..., n-1}` into
non-empty blocks of consecutive integers, where the `iⱼ` are the lengths of the blocks.
This notion is closely related to that of a partition of `n`, but in a composition of `n` the
order of the `iⱼ`s matters.

We implement two different structures covering these two viewpoints on compositions. The first
one, made of a list of positive integers summing to `n`, is the main one and is called
`composition n`. The second one is useful for combinatorial arguments (for instance to show that
the number of compositions of `n` is `2^(n-1)`). It is given by a subset of `{0, ..., n}`
containing `0` and `n`, where the elements of the subset (other than `n`) correspond to the leftmost
points of each block. The main API is built on `composition n`, and we provide an equivalence
between the two types.

## Main functions

* `c : composition n` is a structure, made of a list of integers which are all positive and
  add up to `n`.
* `composition_card` states that the cardinality of `composition n` is exactly
  `2^(n-1)`, which is proved by constructing an equiv with `composition_as_set n` (see below), which
  is itself in bijection with the subsets of `fin (n-1)` (this holds even for `n = 0`, where `-` is
  nat subtraction).

Let `c : composition n` be a composition of `n`. Then
* `c.blocks` is the list of blocks in `c`.
* `c.length` is the number of blocks in the composition.
* `c.blocks_fun : fin c.length → ℕ` is the realization of `c.blocks` as a function on
  `fin c.length`. This is the main object when using compositions to understand the composition of
    analytic functions.
* `c.size_up_to : ℕ → ℕ` is the sum of the size of the blocks up to `i`.;
* `c.embedding i : fin (c.blocks_fun i) → fin n` is the increasing embedding of the `i`-th block in
  `fin n`;
* `c.index j`, for `j : fin n`, is the index of the block containing `j`.

* `composition.ones n` is the composition of `n` made of ones, i.e., `[1, ..., 1]`.
* `composition.single n (hn : 0 < n)` is the composition of `n` made of a single block of size `n`.

Compositions can also be used to split lists. Let `l` be a list of length `n` and `c` a composition
of `n`.
* `l.split_wrt_composition c` is a list of lists, made of the slices of `l` corresponding to the
  blocks of `c`.
* `join_split_wrt_composition` states that splitting a list and then joining it gives back the
  original list.
* `split_wrt_composition_join` states that joining a list of lists, and then splitting it back
  according to the right composition, gives back the original list of lists.

We turn to the second viewpoint on compositions, that we realize as a finset of `fin (n+1)`.
`c : composition_as_set n` is a structure made of a finset of `fin (n+1)` called `c.boundaries`
and proofs that it contains `0` and `n`. (Taking a finset of `fin n` containing `0` would not
make sense in the edge case `n = 0`, while the previous description works in all cases).
The elements of this set (other than `n`) correspond to leftmost points of blocks.
Thus, there is an equiv between `composition n` and `composition_as_set n`. We
only construct basic API on `composition_as_set` (notably `c.length` and `c.blocks`) to be able
to construct this equiv, called `composition_equiv n`. Since there is a straightforward equiv
between `composition_as_set n` and finsets of `{1, ..., n-1}` (obtained by removing `0` and `n`
from a `composition_as_set` and called `composition_as_set_equiv n`), we deduce that
`composition_as_set n` and `composition n` are both fintypes of cardinality `2^(n - 1)`
(see `composition_as_set_card` and `composition_card`).

## Implementation details

The main motivation for this structure and its API is in the construction of the composition of
formal multilinear series, and the proof that the composition of analytic functions is analytic.

The representation of a composition as a list is very handy as lists are very flexible and already
have a well-developed API.

## Tags

Composition, partition

## References

<https://en.wikipedia.org/wiki/Composition_(combinatorics)>
-/

/-- A composition of `n` is a list of positive integers summing to `n`. -/
structure composition (n : ℕ)
where
  blocks : List ℕ
  blocks_pos : ∀ {i : ℕ}, i ∈ blocks → 0 < i
  blocks_sum : list.sum blocks = n

/-- Combinatorial viewpoint on a composition of `n`, by seeing it as non-empty blocks of
consecutive integers in `{0, ..., n-1}`. We register every block by its left end-point, yielding
a finset containing `0`. As this does not make sense for `n = 0`, we add `n` to this finset, and
get a finset of `{0, ..., n}` containing `0` and `n`. This is the data in the structure
`composition_as_set n`. -/
structure composition_as_set (n : ℕ)
where
  boundaries : finset (fin (Nat.succ n))
  zero_mem : 0 ∈ boundaries
  last_mem : fin.last n ∈ boundaries

protected instance composition_as_set.inhabited {n : ℕ} : Inhabited (composition_as_set n) :=
  { default := composition_as_set.mk finset.univ sorry sorry }

/-!
### Compositions

A composition of an integer `n` is a decomposition `n = i₀ + ... + i_{k-1}` of `n` into a sum of
positive integers.
-/

namespace composition

protected instance has_to_string (n : ℕ) : has_to_string (composition n) :=
  has_to_string.mk fun (c : composition n) => to_string (blocks c)

/-- The length of a composition, i.e., the number of blocks in the composition. -/
def length {n : ℕ} (c : composition n) : ℕ :=
  list.length (blocks c)

theorem blocks_length {n : ℕ} (c : composition n) : list.length (blocks c) = length c :=
  rfl

/-- The blocks of a composition, seen as a function on `fin c.length`. When composing analytic
functions using compositions, this is the main player. -/
def blocks_fun {n : ℕ} (c : composition n) : fin (length c) → ℕ :=
  fun (i : fin (length c)) => list.nth_le (blocks c) ↑i sorry

theorem of_fn_blocks_fun {n : ℕ} (c : composition n) : list.of_fn (blocks_fun c) = blocks c :=
  list.of_fn_nth_le (blocks c)

theorem sum_blocks_fun {n : ℕ} (c : composition n) : (finset.sum finset.univ fun (i : fin (length c)) => blocks_fun c i) = n := sorry

theorem blocks_fun_mem_blocks {n : ℕ} (c : composition n) (i : fin (length c)) : blocks_fun c i ∈ blocks c :=
  list.nth_le_mem (blocks c) (↑i) (blocks_fun._proof_1 c i)

@[simp] theorem one_le_blocks {n : ℕ} (c : composition n) {i : ℕ} (h : i ∈ blocks c) : 1 ≤ i :=
  blocks_pos c h

@[simp] theorem one_le_blocks' {n : ℕ} (c : composition n) {i : ℕ} (h : i < length c) : 1 ≤ list.nth_le (blocks c) i h :=
  one_le_blocks c (list.nth_le_mem (blocks c) i h)

@[simp] theorem blocks_pos' {n : ℕ} (c : composition n) (i : ℕ) (h : i < length c) : 0 < list.nth_le (blocks c) i h :=
  one_le_blocks' c h

theorem one_le_blocks_fun {n : ℕ} (c : composition n) (i : fin (length c)) : 1 ≤ blocks_fun c i :=
  one_le_blocks c (blocks_fun_mem_blocks c i)

theorem length_le {n : ℕ} (c : composition n) : length c ≤ n := sorry

theorem length_pos_of_pos {n : ℕ} (c : composition n) (h : 0 < n) : 0 < length c := sorry

/-- The sum of the sizes of the blocks in a composition up to `i`. -/
def size_up_to {n : ℕ} (c : composition n) (i : ℕ) : ℕ :=
  list.sum (list.take i (blocks c))

@[simp] theorem size_up_to_zero {n : ℕ} (c : composition n) : size_up_to c 0 = 0 := sorry

theorem size_up_to_of_length_le {n : ℕ} (c : composition n) (i : ℕ) (h : length c ≤ i) : size_up_to c i = n := sorry

@[simp] theorem size_up_to_length {n : ℕ} (c : composition n) : size_up_to c (length c) = n :=
  size_up_to_of_length_le c (length c) (le_refl (length c))

theorem size_up_to_le {n : ℕ} (c : composition n) (i : ℕ) : size_up_to c i ≤ n := sorry

theorem size_up_to_succ {n : ℕ} (c : composition n) {i : ℕ} (h : i < length c) : size_up_to c (i + 1) = size_up_to c i + list.nth_le (blocks c) i h := sorry

theorem size_up_to_succ' {n : ℕ} (c : composition n) (i : fin (length c)) : size_up_to c (↑i + 1) = size_up_to c ↑i + blocks_fun c i :=
  size_up_to_succ c (subtype.property i)

theorem size_up_to_strict_mono {n : ℕ} (c : composition n) {i : ℕ} (h : i < length c) : size_up_to c i < size_up_to c (i + 1) := sorry

theorem monotone_size_up_to {n : ℕ} (c : composition n) : monotone (size_up_to c) :=
  list.monotone_sum_take (blocks c)

/-- The `i`-th boundary of a composition, i.e., the leftmost point of the `i`-th block. We include
a virtual point at the right of the last block, to make for a nice equiv with
`composition_as_set n`. -/
def boundary {n : ℕ} (c : composition n) : fin (length c + 1) ↪o fin (n + 1) :=
  order_embedding.of_strict_mono (fun (i : fin (length c + 1)) => { val := size_up_to c ↑i, property := sorry }) sorry

@[simp] theorem boundary_zero {n : ℕ} (c : composition n) : coe_fn (boundary c) 0 = 0 := sorry

@[simp] theorem boundary_last {n : ℕ} (c : composition n) : coe_fn (boundary c) (fin.last (length c)) = fin.last n := sorry

/-- The boundaries of a composition, i.e., the leftmost point of all the blocks. We include
a virtual point at the right of the last block, to make for a nice equiv with
`composition_as_set n`. -/
def boundaries {n : ℕ} (c : composition n) : finset (fin (n + 1)) :=
  finset.map (rel_embedding.to_embedding (boundary c)) finset.univ

theorem card_boundaries_eq_succ_length {n : ℕ} (c : composition n) : finset.card (boundaries c) = length c + 1 := sorry

/-- To `c : composition n`, one can associate a `composition_as_set n` by registering the leftmost
point of each block, and adding a virtual point at the right of the last block. -/
def to_composition_as_set {n : ℕ} (c : composition n) : composition_as_set n :=
  composition_as_set.mk (boundaries c) sorry sorry

/-- The canonical increasing bijection between `fin (c.length + 1)` and `c.boundaries` is
exactly `c.boundary`. -/
theorem order_emb_of_fin_boundaries {n : ℕ} (c : composition n) : finset.order_emb_of_fin (boundaries c) (card_boundaries_eq_succ_length c) = boundary c := sorry

/-- Embedding the `i`-th block of a composition (identified with `fin (c.blocks_fun i)`) into
`fin n` at the relevant position. -/
def embedding {n : ℕ} (c : composition n) (i : fin (length c)) : fin (blocks_fun c i) ↪o fin n :=
  rel_embedding.trans (fin.nat_add (size_up_to c ↑i)) (fin.cast_le sorry)

@[simp] theorem coe_embedding {n : ℕ} (c : composition n) (i : fin (length c)) (j : fin (blocks_fun c i)) : ↑(coe_fn (embedding c i) j) = size_up_to c ↑i + ↑j :=
  rfl

/--
`index_exists` asserts there is some `i` with `j < c.size_up_to (i+1)`.
In the next definition `index` we use `nat.find` to produce the minimal such index.
-/
theorem index_exists {n : ℕ} (c : composition n) {j : ℕ} (h : j < n) : ∃ (i : ℕ), j < size_up_to c (Nat.succ i) ∧ i < length c := sorry

/-- `c.index j` is the index of the block in the composition `c` containing `j`. -/
def index {n : ℕ} (c : composition n) (j : fin n) : fin (length c) :=
  { val := nat.find sorry, property := sorry }

theorem lt_size_up_to_index_succ {n : ℕ} (c : composition n) (j : fin n) : ↑j < size_up_to c ↑(fin.succ (index c j)) :=
  and.left (nat.find_spec (index_exists c (subtype.property j)))

theorem size_up_to_index_le {n : ℕ} (c : composition n) (j : fin n) : size_up_to c ↑(index c j) ≤ ↑j := sorry

/-- Mapping an element `j` of `fin n` to the element in the block containing it, identified with
`fin (c.blocks_fun (c.index j))` through the canonical increasing bijection. -/
def inv_embedding {n : ℕ} (c : composition n) (j : fin n) : fin (blocks_fun c (index c j)) :=
  { val := ↑j - size_up_to c ↑(index c j), property := sorry }

@[simp] theorem coe_inv_embedding {n : ℕ} (c : composition n) (j : fin n) : ↑(inv_embedding c j) = ↑j - size_up_to c ↑(index c j) :=
  rfl

theorem embedding_comp_inv {n : ℕ} (c : composition n) (j : fin n) : coe_fn (embedding c (index c j)) (inv_embedding c j) = j := sorry

theorem mem_range_embedding_iff {n : ℕ} (c : composition n) {j : fin n} {i : fin (length c)} : j ∈ set.range ⇑(embedding c i) ↔ size_up_to c ↑i ≤ ↑j ∧ ↑j < size_up_to c (Nat.succ ↑i) := sorry

/-- The embeddings of different blocks of a composition are disjoint. -/
theorem disjoint_range {n : ℕ} (c : composition n) {i₁ : fin (length c)} {i₂ : fin (length c)} (h : i₁ ≠ i₂) : disjoint (set.range ⇑(embedding c i₁)) (set.range ⇑(embedding c i₂)) := sorry

theorem mem_range_embedding {n : ℕ} (c : composition n) (j : fin n) : j ∈ set.range ⇑(embedding c (index c j)) := sorry

theorem mem_range_embedding_iff' {n : ℕ} (c : composition n) {j : fin n} {i : fin (length c)} : j ∈ set.range ⇑(embedding c i) ↔ i = index c j := sorry

theorem index_embedding {n : ℕ} (c : composition n) (i : fin (length c)) (j : fin (blocks_fun c i)) : index c (coe_fn (embedding c i) j) = i := sorry

theorem inv_embedding_comp {n : ℕ} (c : composition n) (i : fin (length c)) (j : fin (blocks_fun c i)) : ↑(inv_embedding c (coe_fn (embedding c i) j)) = ↑j := sorry

/-- Equivalence between the disjoint union of the blocks (each of them seen as
`fin (c.blocks_fun i)`) with `fin n`. -/
def blocks_fin_equiv {n : ℕ} (c : composition n) : (sigma fun (i : fin (length c)) => fin (blocks_fun c i)) ≃ fin n :=
  equiv.mk
    (fun (x : sigma fun (i : fin (length c)) => fin (blocks_fun c i)) => coe_fn (embedding c (sigma.fst x)) (sigma.snd x))
    (fun (j : fin n) => sigma.mk (index c j) (inv_embedding c j)) sorry sorry

theorem blocks_fun_congr {n₁ : ℕ} {n₂ : ℕ} (c₁ : composition n₁) (c₂ : composition n₂) (i₁ : fin (length c₁)) (i₂ : fin (length c₂)) (hn : n₁ = n₂) (hc : blocks c₁ = blocks c₂) (hi : ↑i₁ = ↑i₂) : blocks_fun c₁ i₁ = blocks_fun c₂ i₂ := sorry

/-- Two compositions (possibly of different integers) coincide if and only if they have the
same sequence of blocks. -/
theorem sigma_eq_iff_blocks_eq {c : sigma fun (n : ℕ) => composition n} {c' : sigma fun (n : ℕ) => composition n} : c = c' ↔ blocks (sigma.snd c) = blocks (sigma.snd c') := sorry

/-! ### The composition `composition.ones` -/

/-- The composition made of blocks all of size `1`. -/
def ones (n : ℕ) : composition n :=
  mk (list.repeat 1 n) sorry sorry

protected instance inhabited {n : ℕ} : Inhabited (composition n) :=
  { default := ones n }

@[simp] theorem ones_length (n : ℕ) : length (ones n) = n :=
  list.length_repeat 1 n

@[simp] theorem ones_blocks (n : ℕ) : blocks (ones n) = list.repeat 1 n :=
  rfl

@[simp] theorem ones_blocks_fun (n : ℕ) (i : fin (length (ones n))) : blocks_fun (ones n) i = 1 := sorry

@[simp] theorem ones_size_up_to (n : ℕ) (i : ℕ) : size_up_to (ones n) i = min i n := sorry

@[simp] theorem ones_embedding {n : ℕ} (i : fin (length (ones n))) (h : 0 < blocks_fun (ones n) i) : coe_fn (embedding (ones n) i) { val := 0, property := h } =
  { val := ↑i, property := lt_of_lt_of_le (subtype.property i) (length_le (ones n)) } := sorry

theorem eq_ones_iff {n : ℕ} {c : composition n} : c = ones n ↔ ∀ (i : ℕ), i ∈ blocks c → i = 1 := sorry

theorem ne_ones_iff {n : ℕ} {c : composition n} : c ≠ ones n ↔ ∃ (i : ℕ), ∃ (H : i ∈ blocks c), 1 < i := sorry

theorem eq_ones_iff_length {n : ℕ} {c : composition n} : c = ones n ↔ length c = n := sorry

theorem eq_ones_iff_le_length {n : ℕ} {c : composition n} : c = ones n ↔ n ≤ length c := sorry

/-! ### The composition `composition.single` -/

/-- The composition made of a single block of size `n`. -/
def single (n : ℕ) (h : 0 < n) : composition n :=
  mk [n] sorry sorry

@[simp] theorem single_length {n : ℕ} (h : 0 < n) : length (single n h) = 1 :=
  rfl

@[simp] theorem single_blocks {n : ℕ} (h : 0 < n) : blocks (single n h) = [n] :=
  rfl

@[simp] theorem single_blocks_fun {n : ℕ} (h : 0 < n) (i : fin (length (single n h))) : blocks_fun (single n h) i = n := sorry

@[simp] theorem single_embedding {n : ℕ} (h : 0 < n) (i : fin n) : coe_fn (embedding (single n h) { val := 0, property := single_length h ▸ zero_lt_one }) i = i := sorry

theorem eq_single_iff_length {n : ℕ} (h : 0 < n) {c : composition n} : c = single n h ↔ length c = 1 := sorry

theorem ne_single_iff {n : ℕ} (hn : 0 < n) {c : composition n} : c ≠ single n hn ↔ ∀ (i : fin (length c)), blocks_fun c i < n := sorry

end composition

/-!
### Splitting a list

Given a list of length `n` and a composition `c` of `n`, one can split `l` into `c.length` sublists
of respective lengths `c.blocks_fun 0`, ..., `c.blocks_fun (c.length-1)`. This is inverse to the
join operation.
-/

namespace list

/-- Auxiliary for `list.split_wrt_composition`. -/
def split_wrt_composition_aux {α : Type u_1} : List α → List ℕ → List (List α) :=
  sorry

/-- Given a list of length `n` and a composition `[i₁, ..., iₖ]` of `n`, split `l` into a list of
`k` lists corresponding to the blocks of the composition, of respective lengths `i₁`, ..., `iₖ`.
This makes sense mostly when `n = l.length`, but this is not necessary for the definition. -/
def split_wrt_composition {n : ℕ} {α : Type u_1} (l : List α) (c : composition n) : List (List α) :=
  split_wrt_composition_aux l (composition.blocks c)

theorem split_wrt_composition_aux_cons {α : Type u_1} (l : List α) (n : ℕ) (ns : List ℕ) : split_wrt_composition_aux l (n :: ns) = take n l :: split_wrt_composition_aux (drop n l) ns := sorry

theorem length_split_wrt_composition_aux {α : Type u_1} (l : List α) (ns : List ℕ) : length (split_wrt_composition_aux l ns) = length ns := sorry

/-- When one splits a list along a composition `c`, the number of sublists thus created is
`c.length`. -/
@[simp] theorem length_split_wrt_composition {n : ℕ} {α : Type u_1} (l : List α) (c : composition n) : length (split_wrt_composition l c) = composition.length c :=
  length_split_wrt_composition_aux l (composition.blocks c)

theorem map_length_split_wrt_composition_aux {α : Type u_1} {ns : List ℕ} {l : List α} : sum ns ≤ length l → map length (split_wrt_composition_aux l ns) = ns := sorry

/-- When one splits a list along a composition `c`, the lengths of the sublists thus created are
given by the block sizes in `c`. -/
theorem map_length_split_wrt_composition {α : Type u_1} (l : List α) (c : composition (length l)) : map length (split_wrt_composition l c) = composition.blocks c :=
  map_length_split_wrt_composition_aux (le_of_eq (composition.blocks_sum c))

theorem length_pos_of_mem_split_wrt_composition {α : Type u_1} {l : List α} {l' : List α} {c : composition (length l)} (h : l' ∈ split_wrt_composition l c) : 0 < length l' := sorry

theorem sum_take_map_length_split_wrt_composition {α : Type u_1} (l : List α) (c : composition (length l)) (i : ℕ) : sum (take i (map length (split_wrt_composition l c))) = composition.size_up_to c i := sorry

theorem nth_le_split_wrt_composition_aux {α : Type u_1} (l : List α) (ns : List ℕ) {i : ℕ} (hi : i < length (split_wrt_composition_aux l ns)) : nth_le (split_wrt_composition_aux l ns) i hi = drop (sum (take i ns)) (take (sum (take (i + 1) ns)) l) := sorry

/-- The `i`-th sublist in the splitting of a list `l` along a composition `c`, is the slice of `l`
between the indices `c.size_up_to i` and `c.size_up_to (i+1)`, i.e., the indices in the `i`-th
block of the composition. -/
theorem nth_le_split_wrt_composition {n : ℕ} {α : Type u_1} (l : List α) (c : composition n) {i : ℕ} (hi : i < length (split_wrt_composition l c)) : nth_le (split_wrt_composition l c) i hi = drop (composition.size_up_to c i) (take (composition.size_up_to c (i + 1)) l) :=
  nth_le_split_wrt_composition_aux l (composition.blocks c) hi

theorem join_split_wrt_composition_aux {α : Type u_1} {ns : List ℕ} {l : List α} : sum ns = length l → join (split_wrt_composition_aux l ns) = l := sorry

/-- If one splits a list along a composition, and then joins the sublists, one gets back the
original list. -/
@[simp] theorem join_split_wrt_composition {α : Type u_1} (l : List α) (c : composition (length l)) : join (split_wrt_composition l c) = l :=
  join_split_wrt_composition_aux (composition.blocks_sum c)

/-- If one joins a list of lists and then splits the join along the right composition, one gets
back the original list of lists. -/
@[simp] theorem split_wrt_composition_join {α : Type u_1} (L : List (List α)) (c : composition (length (join L))) (h : map length L = composition.blocks c) : split_wrt_composition (join L) c = L := sorry

end list

/-!
### Compositions as sets

Combinatorial viewpoints on compositions, seen as finite subsets of `fin (n+1)` containing `0` and
`n`, where the points of the set (other than `n`) correspond to the leftmost points of each block.
-/

/-- Bijection between compositions of `n` and subsets of `{0, ..., n-2}`, defined by
considering the restriction of the subset to `{1, ..., n-1}` and shifting to the left by one. -/
def composition_as_set_equiv (n : ℕ) : composition_as_set n ≃ finset (fin (n - 1)) :=
  equiv.mk
    (fun (c : composition_as_set n) =>
      set.to_finset
        (set_of fun (i : fin (n - 1)) => { val := 1 + ↑i, property := sorry } ∈ composition_as_set.boundaries c))
    (fun (s : finset (fin (n - 1))) =>
      composition_as_set.mk
        (set.to_finset
          (set_of
            fun (i : fin (Nat.succ n)) => i = 0 ∨ i = fin.last n ∨ ∃ (j : fin (n - 1)), ∃ (hj : j ∈ s), ↑i = ↑j + 1))
        sorry sorry)
    sorry sorry

protected instance composition_as_set_fintype (n : ℕ) : fintype (composition_as_set n) :=
  fintype.of_equiv (finset (fin (n - 1))) (equiv.symm (composition_as_set_equiv n))

theorem composition_as_set_card (n : ℕ) : fintype.card (composition_as_set n) = bit0 1 ^ (n - 1) := sorry

namespace composition_as_set

theorem boundaries_nonempty {n : ℕ} (c : composition_as_set n) : finset.nonempty (boundaries c) :=
  Exists.intro 0 (zero_mem c)

theorem card_boundaries_pos {n : ℕ} (c : composition_as_set n) : 0 < finset.card (boundaries c) :=
  iff.mpr finset.card_pos (boundaries_nonempty c)

/-- Number of blocks in a `composition_as_set`. -/
def length {n : ℕ} (c : composition_as_set n) : ℕ :=
  finset.card (boundaries c) - 1

theorem card_boundaries_eq_succ_length {n : ℕ} (c : composition_as_set n) : finset.card (boundaries c) = length c + 1 :=
  iff.mp (nat.sub_eq_iff_eq_add (card_boundaries_pos c)) rfl

theorem length_lt_card_boundaries {n : ℕ} (c : composition_as_set n) : length c < finset.card (boundaries c) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (length c < finset.card (boundaries c))) (card_boundaries_eq_succ_length c)))
    (lt_add_one (length c))

theorem lt_length {n : ℕ} (c : composition_as_set n) (i : fin (length c)) : ↑i + 1 < finset.card (boundaries c) :=
  nat.add_lt_of_lt_sub_right (subtype.property i)

theorem lt_length' {n : ℕ} (c : composition_as_set n) (i : fin (length c)) : ↑i < finset.card (boundaries c) :=
  lt_of_le_of_lt (nat.le_succ ↑i) (lt_length c i)

/-- Canonical increasing bijection from `fin c.boundaries.card` to `c.boundaries`. -/
def boundary {n : ℕ} (c : composition_as_set n) : fin (finset.card (boundaries c)) ↪o fin (n + 1) :=
  finset.order_emb_of_fin (boundaries c) sorry

@[simp] theorem boundary_zero {n : ℕ} (c : composition_as_set n) : coe_fn (boundary c) { val := 0, property := card_boundaries_pos c } = 0 := sorry

@[simp] theorem boundary_length {n : ℕ} (c : composition_as_set n) : coe_fn (boundary c) { val := length c, property := length_lt_card_boundaries c } = fin.last n := sorry

/-- Size of the `i`-th block in a `composition_as_set`, seen as a function on `fin c.length`. -/
def blocks_fun {n : ℕ} (c : composition_as_set n) (i : fin (length c)) : ℕ :=
  ↑(coe_fn (boundary c) { val := ↑i + 1, property := lt_length c i }) -
    ↑(coe_fn (boundary c) { val := ↑i, property := lt_length' c i })

theorem blocks_fun_pos {n : ℕ} (c : composition_as_set n) (i : fin (length c)) : 0 < blocks_fun c i :=
  nat.lt_sub_left_of_add_lt
    (order_embedding.strict_mono (finset.order_emb_of_fin (boundaries c) rfl)
      (nat.lt_succ_self (subtype.val { val := ↑i, property := lt_length' c i })))

/-- List of the sizes of the blocks in a `composition_as_set`. -/
def blocks {n : ℕ} (c : composition_as_set n) : List ℕ :=
  list.of_fn (blocks_fun c)

@[simp] theorem blocks_length {n : ℕ} (c : composition_as_set n) : list.length (blocks c) = length c :=
  list.length_of_fn (blocks_fun c)

theorem blocks_partial_sum {n : ℕ} (c : composition_as_set n) {i : ℕ} (h : i < finset.card (boundaries c)) : list.sum (list.take i (blocks c)) = ↑(coe_fn (boundary c) { val := i, property := h }) := sorry

theorem mem_boundaries_iff_exists_blocks_sum_take_eq {n : ℕ} (c : composition_as_set n) {j : fin (n + 1)} : j ∈ boundaries c ↔ ∃ (i : ℕ), ∃ (H : i < finset.card (boundaries c)), list.sum (list.take i (blocks c)) = ↑j := sorry

theorem blocks_sum {n : ℕ} (c : composition_as_set n) : list.sum (blocks c) = n := sorry

/-- Associating a `composition n` to a `composition_as_set n`, by registering the sizes of the
blocks as a list of positive integers. -/
def to_composition {n : ℕ} (c : composition_as_set n) : composition n :=
  composition.mk (blocks c) sorry (blocks_sum c)

end composition_as_set

/-!
### Equivalence between compositions and compositions as sets

In this section, we explain how to go back and forth between a `composition` and a
`composition_as_set`, by showing that their `blocks` and `length` and `boundaries` correspond to
each other, and construct an equivalence between them called `composition_equiv`.
-/

@[simp] theorem composition.to_composition_as_set_length {n : ℕ} (c : composition n) : composition_as_set.length (composition.to_composition_as_set c) = composition.length c := sorry

@[simp] theorem composition_as_set.to_composition_length {n : ℕ} (c : composition_as_set n) : composition.length (composition_as_set.to_composition c) = composition_as_set.length c := sorry

@[simp] theorem composition.to_composition_as_set_blocks {n : ℕ} (c : composition n) : composition_as_set.blocks (composition.to_composition_as_set c) = composition.blocks c := sorry

@[simp] theorem composition_as_set.to_composition_blocks {n : ℕ} (c : composition_as_set n) : composition.blocks (composition_as_set.to_composition c) = composition_as_set.blocks c :=
  rfl

@[simp] theorem composition_as_set.to_composition_boundaries {n : ℕ} (c : composition_as_set n) : composition.boundaries (composition_as_set.to_composition c) = composition_as_set.boundaries c := sorry

@[simp] theorem composition.to_composition_as_set_boundaries {n : ℕ} (c : composition n) : composition_as_set.boundaries (composition.to_composition_as_set c) = composition.boundaries c :=
  rfl

/-- Equivalence between `composition n` and `composition_as_set n`. -/
def composition_equiv (n : ℕ) : composition n ≃ composition_as_set n :=
  equiv.mk (fun (c : composition n) => composition.to_composition_as_set c)
    (fun (c : composition_as_set n) => composition_as_set.to_composition c) sorry sorry

protected instance composition_fintype (n : ℕ) : fintype (composition n) :=
  fintype.of_equiv (composition_as_set n) (equiv.symm (composition_equiv n))

theorem composition_card (n : ℕ) : fintype.card (composition n) = bit0 1 ^ (n - 1) :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (fintype.card (composition n) = bit0 1 ^ (n - 1))) (Eq.symm (composition_as_set_card n))))
    (fintype.card_congr (composition_equiv n))


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import topology.algebra.module.basic
import topology.instances.real

/-!
# Continuous additive maps are `ℝ`-linear

In this file we prove that a continuous map `f : E →+ F` between two topological vector spaces
over `ℝ` is `ℝ`-linear
-/

variables {E : Type*} [add_comm_group E] [module ℝ E] [topological_space E]
  [has_continuous_smul ℝ E] {F : Type*} [add_comm_group F] [module ℝ F]
  [topological_space F] [has_continuous_smul ℝ F] [t2_space F]

namespace add_monoid_hom

/-- A continuous additive map between two vector spaces over `ℝ` is `ℝ`-linear. -/
lemma map_real_smul (f : E →+ F) (hf : continuous f) (c : ℝ) (x : E) :
  f (c • x) = c • f x :=
suffices (λ c : ℝ, f (c • x)) = λ c : ℝ, c • f x, from _root_.congr_fun this c,
rat.dense_embedding_coe_real.dense.equalizer
  (hf.comp $ continuous_id.smul continuous_const)
  (continuous_id.smul continuous_const)
  (funext $ λ r, f.map_rat_cast_smul ℝ ℝ r x)

/-- Reinterpret a continuous additive homomorphism between two real vector spaces
as a continuous real-linear map. -/
def to_real_linear_map (f : E →+ F) (hf : continuous f) : E →L[ℝ] F :=
⟨{ to_fun := f, map_add' := f.map_add, map_smul' := f.map_real_smul hf }, hf⟩

@[simp] lemma coe_to_real_linear_map (f : E →+ F) (hf : continuous f) :
  ⇑(f.to_real_linear_map hf) = f := rfl

end add_monoid_hom

/-- Reinterpret a continuous additive equivalence between two real vector spaces
as a continuous real-linear map. -/
def add_equiv.to_real_linear_equiv (e : E ≃+ F) (h₁ : continuous e)
  (h₂ : continuous e.symm) : E ≃L[ℝ] F :=
{ .. e,
  .. e.to_add_monoid_hom.to_real_linear_map h₁ }

/-- A topological group carries at most one structure of a topological `ℝ`-module, so for any
topological `ℝ`-algebra `A` (e.g. `A = ℂ`) and any topological group that is both a topological
`ℝ`-module and a topological `A`-module, these structures agree. -/
@[priority 900]
instance real.is_scalar_tower [t2_space E] {A : Type*} [topological_space A]
  [ring A] [algebra ℝ A] [module A E] [has_continuous_smul ℝ A]
  [has_continuous_smul A E] : is_scalar_tower ℝ A E :=
⟨λ r x y, ((smul_add_hom A E).flip y).map_real_smul (continuous_id.smul continuous_const) r x⟩


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

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

/-!
# Fixed points of normal functions

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

We prove various statements about the fixed points of normal ordinal functions. We state them in
three forms: as statements about type-indexed families of normal functions, as statements about
ordinal-indexed families of normal functions, and as statements about a single normal function. For
the most part, the first case encompasses the others.

Moreover, we prove some lemmas about the fixed points of specific normal functions.

## Main definitions and results

* `nfp_family`, `nfp_bfamily`, `nfp`: the next fixed point of a (family of) normal function(s).
* `fp_family_unbounded`, `fp_bfamily_unbounded`, `fp_unbounded`: the (common) fixed points of a
  (family of) normal function(s) are unbounded in the ordinals.
* `deriv_add_eq_mul_omega_add`: a characterization of the derivative of addition.
* `deriv_mul_eq_opow_omega_mul`: a characterization of the derivative of multiplication.
-/

noncomputable section

universe u v

open Function Order

namespace Ordinal

/-! ### Fixed points of type-indexed families of ordinals -/

section

variable {ι : Type u} {f : ι → Ordinal.{max u v} → Ordinal.{max u v}}

#print Ordinal.nfpFamily /-
/-- The next common fixed point, at least `a`, for a family of normal functions.

This is defined for any family of functions, as the supremum of all values reachable by applying
finitely many functions in the family to `a`.

`ordinal.nfp_family_fp` shows this is a fixed point, `ordinal.le_nfp_family` shows it's at
least `a`, and `ordinal.nfp_family_le_fp` shows this is the least ordinal with these properties. -/
def nfpFamily (f : ι → Ordinal → Ordinal) (a) : Ordinal :=
  sup (List.foldr f a)
#align ordinal.nfp_family Ordinal.nfpFamily
-/

#print Ordinal.nfpFamily_eq_sup /-
theorem nfpFamily_eq_sup (f : ι → Ordinal → Ordinal) (a) : nfpFamily f a = sup (List.foldr f a) :=
  rfl
#align ordinal.nfp_family_eq_sup Ordinal.nfpFamily_eq_sup
-/

#print Ordinal.foldr_le_nfpFamily /-
theorem foldr_le_nfpFamily (f : ι → Ordinal → Ordinal) (a l) : List.foldr f a l ≤ nfpFamily f a :=
  le_sup _ _
#align ordinal.foldr_le_nfp_family Ordinal.foldr_le_nfpFamily
-/

/- warning: ordinal.le_nfp_family -> Ordinal.le_nfpFamily is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} (f : ι -> Ordinal.{max u1 u2} -> Ordinal.{max u1 u2}) (a : Ordinal.{max u1 u2}), LE.le.{succ (max u1 u2)} Ordinal.{max u1 u2} (Preorder.toLE.{succ (max u1 u2)} Ordinal.{max u1 u2} (PartialOrder.toPreorder.{succ (max u1 u2)} Ordinal.{max u1 u2} Ordinal.partialOrder.{max u1 u2})) a (Ordinal.nfpFamily.{u1, u2} ι f a)
but is expected to have type
  forall {ι : Type.{u2}} (f : ι -> Ordinal.{max u2 u1} -> Ordinal.{max u2 u1}) (a : Ordinal.{max u2 u1}), LE.le.{max (succ u2) (succ u1)} Ordinal.{max u2 u1} (Preorder.toLE.{max (succ u2) (succ u1)} Ordinal.{max u2 u1} (PartialOrder.toPreorder.{max (succ u2) (succ u1)} Ordinal.{max u2 u1} Ordinal.partialOrder.{max u2 u1})) a (Ordinal.nfpFamily.{u2, u1} ι f a)
Case conversion may be inaccurate. Consider using '#align ordinal.le_nfp_family Ordinal.le_nfpFamilyₓ'. -/
theorem le_nfpFamily (f : ι → Ordinal → Ordinal) (a) : a ≤ nfpFamily f a :=
  le_sup _ []
#align ordinal.le_nfp_family Ordinal.le_nfpFamily

#print Ordinal.lt_nfpFamily /-
theorem lt_nfpFamily {a b} : a < nfpFamily f b ↔ ∃ l, a < List.foldr f b l :=
  lt_sup
#align ordinal.lt_nfp_family Ordinal.lt_nfpFamily
-/

#print Ordinal.nfpFamily_le_iff /-
theorem nfpFamily_le_iff {a b} : nfpFamily f a ≤ b ↔ ∀ l, List.foldr f a l ≤ b :=
  sup_le_iff
#align ordinal.nfp_family_le_iff Ordinal.nfpFamily_le_iff
-/

#print Ordinal.nfpFamily_le /-
theorem nfpFamily_le {a b} : (∀ l, List.foldr f a l ≤ b) → nfpFamily f a ≤ b :=
  sup_le
#align ordinal.nfp_family_le Ordinal.nfpFamily_le
-/

#print Ordinal.nfpFamily_monotone /-
theorem nfpFamily_monotone (hf : ∀ i, Monotone (f i)) : Monotone (nfpFamily f) := fun a b h =>
  sup_le fun l => (List.foldr_monotone hf l h).trans (le_sup _ l)
#align ordinal.nfp_family_monotone Ordinal.nfpFamily_monotone
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print Ordinal.apply_lt_nfpFamily /-
theorem apply_lt_nfpFamily (H : ∀ i, IsNormal (f i)) {a b} (hb : b < nfpFamily f a) (i) :
    f i b < nfpFamily f a :=
  let ⟨l, hl⟩ := lt_nfpFamily.1 hb
  lt_sup.2 ⟨i::l, (H i).StrictMono hl⟩
#align ordinal.apply_lt_nfp_family Ordinal.apply_lt_nfpFamily
-/

#print Ordinal.apply_lt_nfpFamily_iff /-
theorem apply_lt_nfpFamily_iff [Nonempty ι] (H : ∀ i, IsNormal (f i)) {a b} :
    (∀ i, f i b < nfpFamily f a) ↔ b < nfpFamily f a :=
  ⟨fun h =>
    lt_nfpFamily.2 <|
      let ⟨l, hl⟩ := lt_sup.1 <| h <| Classical.arbitrary ι
      ⟨l, ((H _).self_le b).trans_lt hl⟩,
    apply_lt_nfpFamily H⟩
#align ordinal.apply_lt_nfp_family_iff Ordinal.apply_lt_nfpFamily_iff
-/

#print Ordinal.nfpFamily_le_apply /-
theorem nfpFamily_le_apply [Nonempty ι] (H : ∀ i, IsNormal (f i)) {a b} :
    (∃ i, nfpFamily f a ≤ f i b) ↔ nfpFamily f a ≤ b :=
  by
  rw [← not_iff_not]
  push_neg
  exact apply_lt_nfp_family_iff H
#align ordinal.nfp_family_le_apply Ordinal.nfpFamily_le_apply
-/

#print Ordinal.nfpFamily_le_fp /-
theorem nfpFamily_le_fp (H : ∀ i, Monotone (f i)) {a b} (ab : a ≤ b) (h : ∀ i, f i b ≤ b) :
    nfpFamily f a ≤ b :=
  sup_le fun l => by
    by_cases hι : IsEmpty ι
    · skip
      rwa [Unique.eq_default l]
    · haveI := not_isEmpty_iff.1 hι
      induction' l with i l IH generalizing a
      · exact ab
      exact (H i (IH ab)).trans (h i)
#align ordinal.nfp_family_le_fp Ordinal.nfpFamily_le_fp
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print Ordinal.nfpFamily_fp /-
theorem nfpFamily_fp {i} (H : IsNormal (f i)) (a) : f i (nfpFamily f a) = nfpFamily f a :=
  by
  unfold nfp_family
  rw [@is_normal.sup _ H _ _ ⟨[]⟩]
  apply le_antisymm <;> refine' Ordinal.sup_le fun l => _
  · exact le_sup _ (i::l)
  · exact (H.self_le _).trans (le_sup _ _)
#align ordinal.nfp_family_fp Ordinal.nfpFamily_fp
-/

#print Ordinal.apply_le_nfpFamily /-
theorem apply_le_nfpFamily [hι : Nonempty ι] {f : ι → Ordinal → Ordinal} (H : ∀ i, IsNormal (f i))
    {a b} : (∀ i, f i b ≤ nfpFamily f a) ↔ b ≤ nfpFamily f a :=
  by
  refine' ⟨fun h => _, fun h i => _⟩
  · cases' hι with i
    exact ((H i).self_le b).trans (h i)
  rw [← nfp_family_fp (H i)]
  exact (H i).Monotone h
#align ordinal.apply_le_nfp_family Ordinal.apply_le_nfpFamily
-/

/- warning: ordinal.nfp_family_eq_self -> Ordinal.nfpFamily_eq_self is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {f : ι -> Ordinal.{max u1 u2} -> Ordinal.{max u1 u2}} {a : Ordinal.{max u1 u2}}, (forall (i : ι), Eq.{succ (succ (max u1 u2))} Ordinal.{max u1 u2} (f i a) a) -> (Eq.{succ (succ (max u1 u2))} Ordinal.{max u1 u2} (Ordinal.nfpFamily.{u1, u2} ι f a) a)
but is expected to have type
  forall {ι : Type.{u2}} {f : ι -> Ordinal.{max u2 u1} -> Ordinal.{max u2 u1}} {a : Ordinal.{max u2 u1}}, (forall (i : ι), Eq.{succ (succ (max u2 u1))} Ordinal.{max u2 u1} (f i a) a) -> (Eq.{max (succ (succ u2)) (succ (succ u1))} Ordinal.{max u1 u2} (Ordinal.nfpFamily.{u2, u1} ι f a) a)
Case conversion may be inaccurate. Consider using '#align ordinal.nfp_family_eq_self Ordinal.nfpFamily_eq_selfₓ'. -/
theorem nfpFamily_eq_self {f : ι → Ordinal → Ordinal} {a} (h : ∀ i, f i a = a) :
    nfpFamily f a = a :=
  le_antisymm (sup_le fun l => by rw [List.foldr_fixed' h l]) <| le_nfpFamily f a
#align ordinal.nfp_family_eq_self Ordinal.nfpFamily_eq_self

#print Ordinal.fp_family_unbounded /-
-- Todo: This is actually a special case of the fact the intersection of club sets is a club set.
/-- A generalization of the fixed point lemma for normal functions: any family of normal functions
    has an unbounded set of common fixed points. -/
theorem fp_family_unbounded (H : ∀ i, IsNormal (f i)) :
    (⋂ i, Function.fixedPoints (f i)).Unbounded (· < ·) := fun a =>
  ⟨_, fun s ⟨i, hi⟩ => by
    rw [← hi]
    exact nfp_family_fp (H i) a, (le_nfpFamily f a).not_lt⟩
#align ordinal.fp_family_unbounded Ordinal.fp_family_unbounded
-/

#print Ordinal.derivFamily /-
/-- The derivative of a family of normal functions is the sequence of their common fixed points.

This is defined for all functions such that `ordinal.deriv_family_zero`,
`ordinal.deriv_family_succ`, and `ordinal.deriv_family_limit` are satisfied. -/
def derivFamily (f : ι → Ordinal → Ordinal) (o : Ordinal) : Ordinal :=
  limitRecOn o (nfpFamily f 0) (fun a IH => nfpFamily f (succ IH)) fun a l => bsup.{max u v, u} a
#align ordinal.deriv_family Ordinal.derivFamily
-/

#print Ordinal.derivFamily_zero /-
@[simp]
theorem derivFamily_zero (f : ι → Ordinal → Ordinal) : derivFamily f 0 = nfpFamily f 0 :=
  limitRecOn_zero _ _ _
#align ordinal.deriv_family_zero Ordinal.derivFamily_zero
-/

#print Ordinal.derivFamily_succ /-
@[simp]
theorem derivFamily_succ (f : ι → Ordinal → Ordinal) (o) :
    derivFamily f (succ o) = nfpFamily f (succ (derivFamily f o)) :=
  limitRecOn_succ _ _ _ _
#align ordinal.deriv_family_succ Ordinal.derivFamily_succ
-/

#print Ordinal.derivFamily_limit /-
theorem derivFamily_limit (f : ι → Ordinal → Ordinal) {o} :
    IsLimit o → derivFamily f o = bsup.{max u v, u} o fun a _ => derivFamily f a :=
  limitRecOn_limit _ _ _ _
#align ordinal.deriv_family_limit Ordinal.derivFamily_limit
-/

/- warning: ordinal.deriv_family_is_normal -> Ordinal.derivFamily_isNormal is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} (f : ι -> Ordinal.{max u1 u2} -> Ordinal.{max u1 u2}), Ordinal.IsNormal.{max u1 u2, max u1 u2} (Ordinal.derivFamily.{u1, u2} ι f)
but is expected to have type
  forall {ι : Type.{u2}} (f : ι -> Ordinal.{max u2 u1} -> Ordinal.{max u2 u1}), Ordinal.IsNormal.{max u1 u2, max u1 u2} (Ordinal.derivFamily.{u2, u1} ι f)
Case conversion may be inaccurate. Consider using '#align ordinal.deriv_family_is_normal Ordinal.derivFamily_isNormalₓ'. -/
theorem derivFamily_isNormal (f : ι → Ordinal → Ordinal) : IsNormal (derivFamily f) :=
  ⟨fun o => by rw [deriv_family_succ, ← succ_le_iff] <;> apply le_nfp_family, fun o l a => by
    rw [deriv_family_limit _ l, bsup_le_iff]⟩
#align ordinal.deriv_family_is_normal Ordinal.derivFamily_isNormal

#print Ordinal.derivFamily_fp /-
theorem derivFamily_fp {i} (H : IsNormal (f i)) (o : Ordinal.{max u v}) :
    f i (derivFamily f o) = derivFamily f o :=
  by
  refine' limit_rec_on o _ (fun o IH => _) _
  · rw [deriv_family_zero]
    exact nfp_family_fp H 0
  · rw [deriv_family_succ]
    exact nfp_family_fp H _
  · intro o l IH
    rw [deriv_family_limit _ l,
      IsNormal.bsup.{max u v, u, max u v} H (fun a _ => deriv_family f a) l.1]
    refine' eq_of_forall_ge_iff fun c => _
    simp (config := { contextual := true }) only [bsup_le_iff, IH]
#align ordinal.deriv_family_fp Ordinal.derivFamily_fp
-/

#print Ordinal.le_iff_derivFamily /-
theorem le_iff_derivFamily (H : ∀ i, IsNormal (f i)) {a} :
    (∀ i, f i a ≤ a) ↔ ∃ o, derivFamily f o = a :=
  ⟨fun ha => by
    suffices : ∀ (o) (_ : a ≤ deriv_family f o), ∃ o, deriv_family f o = a
    exact this a ((deriv_family_is_normal _).self_le _)
    refine' fun o =>
      limit_rec_on o (fun h₁ => ⟨0, le_antisymm _ h₁⟩) (fun o IH h₁ => _) fun o l IH h₁ => _
    · rw [deriv_family_zero]
      exact nfp_family_le_fp (fun i => (H i).Monotone) (Ordinal.zero_le _) ha
    · cases le_or_lt a (deriv_family f o)
      · exact IH h
      refine' ⟨succ o, le_antisymm _ h₁⟩
      rw [deriv_family_succ]
      exact nfp_family_le_fp (fun i => (H i).Monotone) (succ_le_of_lt h) ha
    · cases eq_or_lt_of_le h₁
      · exact ⟨_, h.symm⟩
      rw [deriv_family_limit _ l, ← not_le, bsup_le_iff, not_ball] at h
      exact
        let ⟨o', h, hl⟩ := h
        IH o' h (le_of_not_le hl),
    fun ⟨o, e⟩ i => e ▸ (derivFamily_fp (H i) _).le⟩
#align ordinal.le_iff_deriv_family Ordinal.le_iff_derivFamily
-/

#print Ordinal.fp_iff_derivFamily /-
theorem fp_iff_derivFamily (H : ∀ i, IsNormal (f i)) {a} :
    (∀ i, f i a = a) ↔ ∃ o, derivFamily f o = a :=
  Iff.trans ⟨fun h i => le_of_eq (h i), fun h i => (H i).le_iff_eq.1 (h i)⟩ (le_iff_derivFamily H)
#align ordinal.fp_iff_deriv_family Ordinal.fp_iff_derivFamily
-/

#print Ordinal.derivFamily_eq_enumOrd /-
/-- For a family of normal functions, `ordinal.deriv_family` enumerates the common fixed points. -/
theorem derivFamily_eq_enumOrd (H : ∀ i, IsNormal (f i)) :
    derivFamily f = enumOrd (⋂ i, Function.fixedPoints (f i)) :=
  by
  rw [← eq_enum_ord _ (fp_family_unbounded H)]
  use (deriv_family_is_normal f).StrictMono
  rw [Set.range_eq_iff]
  refine' ⟨_, fun a ha => _⟩
  · rintro a S ⟨i, hi⟩
    rw [← hi]
    exact deriv_family_fp (H i) a
  rw [Set.mem_interᵢ] at ha
  rwa [← fp_iff_deriv_family H]
#align ordinal.deriv_family_eq_enum_ord Ordinal.derivFamily_eq_enumOrd
-/

end

/-! ### Fixed points of ordinal-indexed families of ordinals -/

section

variable {o : Ordinal.{u}} {f : ∀ b < o, Ordinal.{max u v} → Ordinal.{max u v}}

#print Ordinal.nfpBFamily /-
/-- The next common fixed point, at least `a`, for a family of normal functions indexed by ordinals.

This is defined as `ordinal.nfp_family` of the type-indexed family associated to `f`.
-/
def nfpBFamily (o : Ordinal) (f : ∀ b < o, Ordinal → Ordinal) : Ordinal → Ordinal :=
  nfpFamily (familyOfBFamily o f)
#align ordinal.nfp_bfamily Ordinal.nfpBFamily
-/

#print Ordinal.nfpBFamily_eq_nfpFamily /-
theorem nfpBFamily_eq_nfpFamily {o : Ordinal} (f : ∀ b < o, Ordinal → Ordinal) :
    nfpBFamily o f = nfpFamily (familyOfBFamily o f) :=
  rfl
#align ordinal.nfp_bfamily_eq_nfp_family Ordinal.nfpBFamily_eq_nfpFamily
-/

#print Ordinal.foldr_le_nfpBFamily /-
theorem foldr_le_nfpBFamily {o : Ordinal} (f : ∀ b < o, Ordinal → Ordinal) (a l) :
    List.foldr (familyOfBFamily o f) a l ≤ nfpBFamily o f a :=
  le_sup _ _
#align ordinal.foldr_le_nfp_bfamily Ordinal.foldr_le_nfpBFamily
-/

#print Ordinal.le_nfpBFamily /-
theorem le_nfpBFamily {o : Ordinal} (f : ∀ b < o, Ordinal → Ordinal) (a) : a ≤ nfpBFamily o f a :=
  le_sup _ []
#align ordinal.le_nfp_bfamily Ordinal.le_nfpBFamily
-/

#print Ordinal.lt_nfpBFamily /-
theorem lt_nfpBFamily {a b} :
    a < nfpBFamily o f b ↔ ∃ l, a < List.foldr (familyOfBFamily o f) b l :=
  lt_sup
#align ordinal.lt_nfp_bfamily Ordinal.lt_nfpBFamily
-/

#print Ordinal.nfpBFamily_le_iff /-
theorem nfpBFamily_le_iff {o : Ordinal} {f : ∀ b < o, Ordinal → Ordinal} {a b} :
    nfpBFamily o f a ≤ b ↔ ∀ l, List.foldr (familyOfBFamily o f) a l ≤ b :=
  sup_le_iff
#align ordinal.nfp_bfamily_le_iff Ordinal.nfpBFamily_le_iff
-/

#print Ordinal.nfpBFamily_le /-
theorem nfpBFamily_le {o : Ordinal} {f : ∀ b < o, Ordinal → Ordinal} {a b} :
    (∀ l, List.foldr (familyOfBFamily o f) a l ≤ b) → nfpBFamily o f a ≤ b :=
  sup_le
#align ordinal.nfp_bfamily_le Ordinal.nfpBFamily_le
-/

#print Ordinal.nfpBFamily_monotone /-
theorem nfpBFamily_monotone (hf : ∀ i hi, Monotone (f i hi)) : Monotone (nfpBFamily o f) :=
  nfpFamily_monotone fun i => hf _ _
#align ordinal.nfp_bfamily_monotone Ordinal.nfpBFamily_monotone
-/

#print Ordinal.apply_lt_nfpBFamily /-
theorem apply_lt_nfpBFamily (H : ∀ i hi, IsNormal (f i hi)) {a b} (hb : b < nfpBFamily o f a)
    (i hi) : f i hi b < nfpBFamily o f a :=
  by
  rw [← family_of_bfamily_enum o f]
  apply apply_lt_nfp_family _ hb
  exact fun _ => H _ _
#align ordinal.apply_lt_nfp_bfamily Ordinal.apply_lt_nfpBFamily
-/

#print Ordinal.apply_lt_nfpBFamily_iff /-
theorem apply_lt_nfpBFamily_iff (ho : o ≠ 0) (H : ∀ i hi, IsNormal (f i hi)) {a b} :
    (∀ i hi, f i hi b < nfpBFamily o f a) ↔ b < nfpBFamily o f a :=
  ⟨fun h => by
    haveI := out_nonempty_iff_ne_zero.2 ho
    refine' (apply_lt_nfp_family_iff _).1 fun _ => h _ _
    exact fun _ => H _ _, apply_lt_nfpBFamily H⟩
#align ordinal.apply_lt_nfp_bfamily_iff Ordinal.apply_lt_nfpBFamily_iff
-/

#print Ordinal.nfpBFamily_le_apply /-
theorem nfpBFamily_le_apply (ho : o ≠ 0) (H : ∀ i hi, IsNormal (f i hi)) {a b} :
    (∃ i hi, nfpBFamily o f a ≤ f i hi b) ↔ nfpBFamily o f a ≤ b :=
  by
  rw [← not_iff_not]
  push_neg
  convert apply_lt_nfp_bfamily_iff ho H
  simp only [not_le]
#align ordinal.nfp_bfamily_le_apply Ordinal.nfpBFamily_le_apply
-/

#print Ordinal.nfpBFamily_le_fp /-
theorem nfpBFamily_le_fp (H : ∀ i hi, Monotone (f i hi)) {a b} (ab : a ≤ b)
    (h : ∀ i hi, f i hi b ≤ b) : nfpBFamily o f a ≤ b :=
  nfpFamily_le_fp (fun _ => H _ _) ab fun i => h _ _
#align ordinal.nfp_bfamily_le_fp Ordinal.nfpBFamily_le_fp
-/

#print Ordinal.nfpBFamily_fp /-
theorem nfpBFamily_fp {i hi} (H : IsNormal (f i hi)) (a) :
    f i hi (nfpBFamily o f a) = nfpBFamily o f a :=
  by
  rw [← family_of_bfamily_enum o f]
  apply nfp_family_fp
  rw [family_of_bfamily_enum]
  exact H
#align ordinal.nfp_bfamily_fp Ordinal.nfpBFamily_fp
-/

#print Ordinal.apply_le_nfpBFamily /-
theorem apply_le_nfpBFamily (ho : o ≠ 0) (H : ∀ i hi, IsNormal (f i hi)) {a b} :
    (∀ i hi, f i hi b ≤ nfpBFamily o f a) ↔ b ≤ nfpBFamily o f a :=
  by
  refine' ⟨fun h => _, fun h i hi => _⟩
  · have ho' : 0 < o := Ordinal.pos_iff_ne_zero.2 ho
    exact ((H 0 ho').self_le b).trans (h 0 ho')
  · rw [← nfp_bfamily_fp (H i hi)]
    exact (H i hi).Monotone h
#align ordinal.apply_le_nfp_bfamily Ordinal.apply_le_nfpBFamily
-/

#print Ordinal.nfpBFamily_eq_self /-
theorem nfpBFamily_eq_self {a} (h : ∀ i hi, f i hi a = a) : nfpBFamily o f a = a :=
  nfpFamily_eq_self fun _ => h _ _
#align ordinal.nfp_bfamily_eq_self Ordinal.nfpBFamily_eq_self
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:107:6: warning: expanding binder group (i hi) -/
#print Ordinal.fp_bfamily_unbounded /-
/-- A generalization of the fixed point lemma for normal functions: any family of normal functions
    has an unbounded set of common fixed points. -/
theorem fp_bfamily_unbounded (H : ∀ i hi, IsNormal (f i hi)) :
    (⋂ (i) (hi), Function.fixedPoints (f i hi)).Unbounded (· < ·) := fun a =>
  ⟨_, by
    rw [Set.mem_interᵢ₂]
    exact fun i hi => nfp_bfamily_fp (H i hi) _, (le_nfpBFamily f a).not_lt⟩
#align ordinal.fp_bfamily_unbounded Ordinal.fp_bfamily_unbounded
-/

#print Ordinal.derivBFamily /-
/-- The derivative of a family of normal functions is the sequence of their common fixed points.

This is defined as `ordinal.deriv_family` of the type-indexed family associated to `f`. -/
def derivBFamily (o : Ordinal) (f : ∀ b < o, Ordinal → Ordinal) : Ordinal → Ordinal :=
  derivFamily (familyOfBFamily o f)
#align ordinal.deriv_bfamily Ordinal.derivBFamily
-/

#print Ordinal.derivBFamily_eq_derivFamily /-
theorem derivBFamily_eq_derivFamily {o : Ordinal} (f : ∀ b < o, Ordinal → Ordinal) :
    derivBFamily o f = derivFamily (familyOfBFamily o f) :=
  rfl
#align ordinal.deriv_bfamily_eq_deriv_family Ordinal.derivBFamily_eq_derivFamily
-/

/- warning: ordinal.deriv_bfamily_is_normal -> Ordinal.derivBFamily_isNormal is a dubious translation:
lean 3 declaration is
  forall {o : Ordinal.{u1}} (f : forall (b : Ordinal.{u1}), (LT.lt.{succ u1} Ordinal.{u1} (Preorder.toLT.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) b o) -> Ordinal.{max u1 u2} -> Ordinal.{max u1 u2}), Ordinal.IsNormal.{max u1 u2, max u1 u2} (Ordinal.derivBFamily.{u1, u2} o f)
but is expected to have type
  forall {o : Ordinal.{u2}} (f : forall (b : Ordinal.{u2}), (LT.lt.{succ u2} Ordinal.{u2} (Preorder.toLT.{succ u2} Ordinal.{u2} (PartialOrder.toPreorder.{succ u2} Ordinal.{u2} Ordinal.partialOrder.{u2})) b o) -> Ordinal.{max u2 u1} -> Ordinal.{max u2 u1}), Ordinal.IsNormal.{max u1 u2, max u1 u2} (Ordinal.derivBFamily.{u2, u1} o f)
Case conversion may be inaccurate. Consider using '#align ordinal.deriv_bfamily_is_normal Ordinal.derivBFamily_isNormalₓ'. -/
theorem derivBFamily_isNormal {o : Ordinal} (f : ∀ b < o, Ordinal → Ordinal) :
    IsNormal (derivBFamily o f) :=
  derivFamily_isNormal _
#align ordinal.deriv_bfamily_is_normal Ordinal.derivBFamily_isNormal

#print Ordinal.derivBFamily_fp /-
theorem derivBFamily_fp {i hi} (H : IsNormal (f i hi)) (a : Ordinal) :
    f i hi (derivBFamily o f a) = derivBFamily o f a :=
  by
  rw [← family_of_bfamily_enum o f]
  apply deriv_family_fp
  rw [family_of_bfamily_enum]
  exact H
#align ordinal.deriv_bfamily_fp Ordinal.derivBFamily_fp
-/

#print Ordinal.le_iff_derivBFamily /-
theorem le_iff_derivBFamily (H : ∀ i hi, IsNormal (f i hi)) {a} :
    (∀ i hi, f i hi a ≤ a) ↔ ∃ b, derivBFamily o f b = a :=
  by
  unfold deriv_bfamily
  rw [← le_iff_deriv_family]
  · refine' ⟨fun h i => h _ _, fun h i hi => _⟩
    rw [← family_of_bfamily_enum o f]
    apply h
  · exact fun _ => H _ _
#align ordinal.le_iff_deriv_bfamily Ordinal.le_iff_derivBFamily
-/

#print Ordinal.fp_iff_derivBFamily /-
theorem fp_iff_derivBFamily (H : ∀ i hi, IsNormal (f i hi)) {a} :
    (∀ i hi, f i hi a = a) ↔ ∃ b, derivBFamily o f b = a :=
  by
  rw [← le_iff_deriv_bfamily H]
  refine' ⟨fun h i hi => le_of_eq (h i hi), fun h i hi => _⟩
  rw [← (H i hi).le_iff_eq]
  exact h i hi
#align ordinal.fp_iff_deriv_bfamily Ordinal.fp_iff_derivBFamily
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:107:6: warning: expanding binder group (i hi) -/
#print Ordinal.derivBFamily_eq_enumOrd /-
/-- For a family of normal functions, `ordinal.deriv_bfamily` enumerates the common fixed points. -/
theorem derivBFamily_eq_enumOrd (H : ∀ i hi, IsNormal (f i hi)) :
    derivBFamily o f = enumOrd (⋂ (i) (hi), Function.fixedPoints (f i hi)) :=
  by
  rw [← eq_enum_ord _ (fp_bfamily_unbounded H)]
  use (deriv_bfamily_is_normal f).StrictMono
  rw [Set.range_eq_iff]
  refine' ⟨fun a => Set.mem_interᵢ₂.2 fun i hi => deriv_bfamily_fp (H i hi) a, fun a ha => _⟩
  rw [Set.mem_interᵢ₂] at ha
  rwa [← fp_iff_deriv_bfamily H]
#align ordinal.deriv_bfamily_eq_enum_ord Ordinal.derivBFamily_eq_enumOrd
-/

end

/-! ### Fixed points of a single function -/

section

variable {f : Ordinal.{u} → Ordinal.{u}}

#print Ordinal.nfp /-
/-- The next fixed point function, the least fixed point of the normal function `f`, at least `a`.

This is defined as `ordinal.nfp_family` applied to a family consisting only of `f`. -/
def nfp (f : Ordinal → Ordinal) : Ordinal → Ordinal :=
  nfpFamily fun _ : Unit => f
#align ordinal.nfp Ordinal.nfp
-/

#print Ordinal.nfp_eq_nfpFamily /-
theorem nfp_eq_nfpFamily (f : Ordinal → Ordinal) : nfp f = nfpFamily fun _ : Unit => f :=
  rfl
#align ordinal.nfp_eq_nfp_family Ordinal.nfp_eq_nfpFamily
-/

#print Ordinal.sup_iterate_eq_nfp /-
@[simp]
theorem sup_iterate_eq_nfp (f : Ordinal.{u} → Ordinal.{u}) :
    (fun a => sup fun n : ℕ => (f^[n]) a) = nfp f :=
  by
  refine' funext fun a => le_antisymm _ (sup_le fun l => _)
  · rw [sup_le_iff]
    intro n
    rw [← List.length_replicate n Unit.unit, ← List.foldr_const f a]
    apply le_sup
  · rw [List.foldr_const f a l]
    exact le_sup _ _
#align ordinal.sup_iterate_eq_nfp Ordinal.sup_iterate_eq_nfp
-/

#print Ordinal.iterate_le_nfp /-
theorem iterate_le_nfp (f a n) : (f^[n]) a ≤ nfp f a :=
  by
  rw [← sup_iterate_eq_nfp]
  exact le_sup _ n
#align ordinal.iterate_le_nfp Ordinal.iterate_le_nfp
-/

#print Ordinal.le_nfp /-
theorem le_nfp (f a) : a ≤ nfp f a :=
  iterate_le_nfp f a 0
#align ordinal.le_nfp Ordinal.le_nfp
-/

#print Ordinal.lt_nfp /-
theorem lt_nfp {a b} : a < nfp f b ↔ ∃ n, a < (f^[n]) b :=
  by
  rw [← sup_iterate_eq_nfp]
  exact lt_sup
#align ordinal.lt_nfp Ordinal.lt_nfp
-/

#print Ordinal.nfp_le_iff /-
theorem nfp_le_iff {a b} : nfp f a ≤ b ↔ ∀ n, (f^[n]) a ≤ b :=
  by
  rw [← sup_iterate_eq_nfp]
  exact sup_le_iff
#align ordinal.nfp_le_iff Ordinal.nfp_le_iff
-/

#print Ordinal.nfp_le /-
theorem nfp_le {a b} : (∀ n, (f^[n]) a ≤ b) → nfp f a ≤ b :=
  nfp_le_iff.2
#align ordinal.nfp_le Ordinal.nfp_le
-/

#print Ordinal.nfp_id /-
@[simp]
theorem nfp_id : nfp id = id :=
  funext fun a => by
    simp_rw [← sup_iterate_eq_nfp, iterate_id]
    exact sup_const a
#align ordinal.nfp_id Ordinal.nfp_id
-/

#print Ordinal.nfp_monotone /-
theorem nfp_monotone (hf : Monotone f) : Monotone (nfp f) :=
  nfpFamily_monotone fun i => hf
#align ordinal.nfp_monotone Ordinal.nfp_monotone
-/

#print Ordinal.IsNormal.apply_lt_nfp /-
theorem IsNormal.apply_lt_nfp {f} (H : IsNormal f) {a b} : f b < nfp f a ↔ b < nfp f a :=
  by
  unfold nfp
  rw [← @apply_lt_nfp_family_iff Unit (fun _ => f) _ (fun _ => H) a b]
  exact ⟨fun h _ => h, fun h => h Unit.unit⟩
#align ordinal.is_normal.apply_lt_nfp Ordinal.IsNormal.apply_lt_nfp
-/

#print Ordinal.IsNormal.nfp_le_apply /-
theorem IsNormal.nfp_le_apply {f} (H : IsNormal f) {a b} : nfp f a ≤ f b ↔ nfp f a ≤ b :=
  le_iff_le_iff_lt_iff_lt.2 H.apply_lt_nfp
#align ordinal.is_normal.nfp_le_apply Ordinal.IsNormal.nfp_le_apply
-/

#print Ordinal.nfp_le_fp /-
theorem nfp_le_fp {f} (H : Monotone f) {a b} (ab : a ≤ b) (h : f b ≤ b) : nfp f a ≤ b :=
  nfpFamily_le_fp (fun _ => H) ab fun _ => h
#align ordinal.nfp_le_fp Ordinal.nfp_le_fp
-/

#print Ordinal.IsNormal.nfp_fp /-
theorem IsNormal.nfp_fp {f} (H : IsNormal f) : ∀ a, f (nfp f a) = nfp f a :=
  @nfpFamily_fp Unit (fun _ => f) Unit.unit H
#align ordinal.is_normal.nfp_fp Ordinal.IsNormal.nfp_fp
-/

#print Ordinal.IsNormal.apply_le_nfp /-
theorem IsNormal.apply_le_nfp {f} (H : IsNormal f) {a b} : f b ≤ nfp f a ↔ b ≤ nfp f a :=
  ⟨le_trans (H.self_le _), fun h => by simpa only [H.nfp_fp] using H.le_iff.2 h⟩
#align ordinal.is_normal.apply_le_nfp Ordinal.IsNormal.apply_le_nfp
-/

#print Ordinal.nfp_eq_self /-
theorem nfp_eq_self {f : Ordinal → Ordinal} {a} (h : f a = a) : nfp f a = a :=
  nfpFamily_eq_self fun _ => h
#align ordinal.nfp_eq_self Ordinal.nfp_eq_self
-/

#print Ordinal.fp_unbounded /-
/-- The fixed point lemma for normal functions: any normal function has an unbounded set of
fixed points. -/
theorem fp_unbounded (H : IsNormal f) : (Function.fixedPoints f).Unbounded (· < ·) :=
  by
  convert fp_family_unbounded fun _ : Unit => H
  exact (Set.interᵢ_const _).symm
#align ordinal.fp_unbounded Ordinal.fp_unbounded
-/

#print Ordinal.deriv /-
/-- The derivative of a normal function `f` is the sequence of fixed points of `f`.

This is defined as `ordinal.deriv_family` applied to a trivial family consisting only of `f`. -/
def deriv (f : Ordinal → Ordinal) : Ordinal → Ordinal :=
  derivFamily fun _ : Unit => f
#align ordinal.deriv Ordinal.deriv
-/

#print Ordinal.deriv_eq_derivFamily /-
theorem deriv_eq_derivFamily (f : Ordinal → Ordinal) : deriv f = derivFamily fun _ : Unit => f :=
  rfl
#align ordinal.deriv_eq_deriv_family Ordinal.deriv_eq_derivFamily
-/

#print Ordinal.deriv_zero /-
@[simp]
theorem deriv_zero (f) : deriv f 0 = nfp f 0 :=
  derivFamily_zero _
#align ordinal.deriv_zero Ordinal.deriv_zero
-/

#print Ordinal.deriv_succ /-
@[simp]
theorem deriv_succ (f o) : deriv f (succ o) = nfp f (succ (deriv f o)) :=
  derivFamily_succ _ _
#align ordinal.deriv_succ Ordinal.deriv_succ
-/

#print Ordinal.deriv_limit /-
theorem deriv_limit (f) {o} : IsLimit o → deriv f o = bsup.{u, 0} o fun a _ => deriv f a :=
  derivFamily_limit _
#align ordinal.deriv_limit Ordinal.deriv_limit
-/

#print Ordinal.deriv_isNormal /-
theorem deriv_isNormal (f) : IsNormal (deriv f) :=
  derivFamily_isNormal _
#align ordinal.deriv_is_normal Ordinal.deriv_isNormal
-/

#print Ordinal.deriv_id_of_nfp_id /-
theorem deriv_id_of_nfp_id {f : Ordinal → Ordinal} (h : nfp f = id) : deriv f = id :=
  ((deriv_isNormal _).eq_iff_zero_and_succ IsNormal.refl).2 (by simp [h])
#align ordinal.deriv_id_of_nfp_id Ordinal.deriv_id_of_nfp_id
-/

#print Ordinal.IsNormal.deriv_fp /-
theorem IsNormal.deriv_fp {f} (H : IsNormal f) : ∀ o, f (deriv f o) = deriv f o :=
  @derivFamily_fp Unit (fun _ => f) Unit.unit H
#align ordinal.is_normal.deriv_fp Ordinal.IsNormal.deriv_fp
-/

#print Ordinal.IsNormal.le_iff_deriv /-
theorem IsNormal.le_iff_deriv {f} (H : IsNormal f) {a} : f a ≤ a ↔ ∃ o, deriv f o = a :=
  by
  unfold deriv
  rw [← le_iff_deriv_family fun _ : Unit => H]
  exact ⟨fun h _ => h, fun h => h Unit.unit⟩
#align ordinal.is_normal.le_iff_deriv Ordinal.IsNormal.le_iff_deriv
-/

#print Ordinal.IsNormal.fp_iff_deriv /-
theorem IsNormal.fp_iff_deriv {f} (H : IsNormal f) {a} : f a = a ↔ ∃ o, deriv f o = a := by
  rw [← H.le_iff_eq, H.le_iff_deriv]
#align ordinal.is_normal.fp_iff_deriv Ordinal.IsNormal.fp_iff_deriv
-/

#print Ordinal.deriv_eq_enumOrd /-
/-- `ordinal.deriv` enumerates the fixed points of a normal function. -/
theorem deriv_eq_enumOrd (H : IsNormal f) : deriv f = enumOrd (Function.fixedPoints f) :=
  by
  convert deriv_family_eq_enum_ord fun _ : Unit => H
  exact (Set.interᵢ_const _).symm
#align ordinal.deriv_eq_enum_ord Ordinal.deriv_eq_enumOrd
-/

#print Ordinal.deriv_eq_id_of_nfp_eq_id /-
theorem deriv_eq_id_of_nfp_eq_id {f : Ordinal → Ordinal} (h : nfp f = id) : deriv f = id :=
  (IsNormal.eq_iff_zero_and_succ (deriv_isNormal _) IsNormal.refl).2 <| by simp [h]
#align ordinal.deriv_eq_id_of_nfp_eq_id Ordinal.deriv_eq_id_of_nfp_eq_id
-/

end

/-! ### Fixed points of addition -/

/- warning: ordinal.nfp_add_zero -> Ordinal.nfp_add_zero is a dubious translation:
lean 3 declaration is
  forall (a : Ordinal.{u1}), Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{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})))) (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})
but is expected to have type
  forall (a : Ordinal.{u1}), Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.5798 : 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 x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.5798) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{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})
Case conversion may be inaccurate. Consider using '#align ordinal.nfp_add_zero Ordinal.nfp_add_zeroₓ'. -/
@[simp]
theorem nfp_add_zero (a) : nfp ((· + ·) a) 0 = a * omega :=
  by
  simp_rw [← sup_iterate_eq_nfp, ← sup_mul_nat]
  congr ; funext
  induction' n with n hn
  · rw [Nat.cast_zero, MulZeroClass.mul_zero, iterate_zero_apply]
  · nth_rw 2 [Nat.succ_eq_one_add]
    rw [Nat.cast_add, Nat.cast_one, mul_one_add, iterate_succ_apply', hn]
#align ordinal.nfp_add_zero Ordinal.nfp_add_zero

/- warning: ordinal.nfp_add_eq_mul_omega -> Ordinal.nfp_add_eq_mul_omega is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) b (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})) -> (Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1}) a) b) (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}))
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) b (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})) -> (Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.5937 : 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 x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.5937) b) (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}))
Case conversion may be inaccurate. Consider using '#align ordinal.nfp_add_eq_mul_omega Ordinal.nfp_add_eq_mul_omegaₓ'. -/
theorem nfp_add_eq_mul_omega {a b} (hba : b ≤ a * omega) : nfp ((· + ·) a) b = a * omega :=
  by
  apply le_antisymm (nfp_le_fp (add_is_normal a).Monotone hba _)
  · rw [← nfp_add_zero]
    exact nfp_monotone (add_is_normal a).Monotone (Ordinal.zero_le b)
  · rw [← mul_one_add, one_add_omega]
#align ordinal.nfp_add_eq_mul_omega Ordinal.nfp_add_eq_mul_omega

/- warning: ordinal.add_eq_right_iff_mul_omega_le -> Ordinal.add_eq_right_iff_mul_omega_le is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, Iff (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 b) b) (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{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 Ordinal.omega.{u1}) b)
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, Iff (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 b) b) (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{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 Ordinal.omega.{u1}) b)
Case conversion may be inaccurate. Consider using '#align ordinal.add_eq_right_iff_mul_omega_le Ordinal.add_eq_right_iff_mul_omega_leₓ'. -/
theorem add_eq_right_iff_mul_omega_le {a b : Ordinal} : a + b = b ↔ a * omega ≤ b :=
  by
  refine' ⟨fun h => _, fun h => _⟩
  · rw [← nfp_add_zero a, ← deriv_zero]
    cases' (add_is_normal a).fp_iff_deriv.1 h with c hc
    rw [← hc]
    exact (deriv_is_normal _).Monotone (Ordinal.zero_le _)
  · have := Ordinal.add_sub_cancel_of_le h
    nth_rw 1 [← this]
    rwa [← add_assoc, ← mul_one_add, one_add_omega]
#align ordinal.add_eq_right_iff_mul_omega_le Ordinal.add_eq_right_iff_mul_omega_le

/- warning: ordinal.add_le_right_iff_mul_omega_le -> Ordinal.add_le_right_iff_mul_omega_le is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, Iff (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1}) a b) b) (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{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 Ordinal.omega.{u1}) b)
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, Iff (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{u1}) a b) b) (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{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 Ordinal.omega.{u1}) b)
Case conversion may be inaccurate. Consider using '#align ordinal.add_le_right_iff_mul_omega_le Ordinal.add_le_right_iff_mul_omega_leₓ'. -/
theorem add_le_right_iff_mul_omega_le {a b : Ordinal} : a + b ≤ b ↔ a * omega ≤ b :=
  by
  rw [← add_eq_right_iff_mul_omega_le]
  exact (add_is_normal a).le_iff_eq
#align ordinal.add_le_right_iff_mul_omega_le Ordinal.add_le_right_iff_mul_omega_le

/- warning: ordinal.deriv_add_eq_mul_omega_add -> Ordinal.deriv_add_eq_mul_omega_add is a dubious translation:
lean 3 declaration is
  forall (a : Ordinal.{u1}) (b : Ordinal.{u1}), Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.deriv.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.hasAdd.{u1}) a) b) (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 Ordinal.omega.{u1}) b)
but is expected to have type
  forall (a : Ordinal.{u1}) (b : Ordinal.{u1}), Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.deriv.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.6313 : 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 x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.6313) b) (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{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}) b)
Case conversion may be inaccurate. Consider using '#align ordinal.deriv_add_eq_mul_omega_add Ordinal.deriv_add_eq_mul_omega_addₓ'. -/
theorem deriv_add_eq_mul_omega_add (a b : Ordinal.{u}) : deriv ((· + ·) a) b = a * omega + b :=
  by
  revert b
  rw [← funext_iff, is_normal.eq_iff_zero_and_succ (deriv_is_normal _) (add_is_normal _)]
  refine' ⟨_, fun a h => _⟩
  · rw [deriv_zero, add_zero]
    exact nfp_add_zero a
  · rw [deriv_succ, h, add_succ]
    exact nfp_eq_self (add_eq_right_iff_mul_omega_le.2 ((le_add_right _ _).trans (le_succ _)))
#align ordinal.deriv_add_eq_mul_omega_add Ordinal.deriv_add_eq_mul_omega_add

/-! ### Fixed points of multiplication -/

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

/- warning: ordinal.nfp_mul_one -> Ordinal.nfp_mul_one 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) -> (Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{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) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{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}))
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) -> (Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.6482 : 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 x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.6482) (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{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}))
Case conversion may be inaccurate. Consider using '#align ordinal.nfp_mul_one Ordinal.nfp_mul_oneₓ'. -/
@[simp]
theorem nfp_mul_one {a : Ordinal} (ha : 0 < a) : nfp ((· * ·) a) 1 = (a^omega) :=
  by
  rw [← sup_iterate_eq_nfp, ← sup_opow_nat]
  · dsimp
    congr
    funext
    induction' n with n hn
    · rw [Nat.cast_zero, opow_zero, iterate_zero_apply]
    nth_rw 2 [Nat.succ_eq_one_add]
    rw [Nat.cast_add, Nat.cast_one, opow_add, opow_one, iterate_succ_apply', hn]
  · exact ha
#align ordinal.nfp_mul_one Ordinal.nfp_mul_one

/- warning: ordinal.nfp_mul_zero -> Ordinal.nfp_mul_zero is a dubious translation:
lean 3 declaration is
  forall (a : Ordinal.{u1}), Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{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) (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 (a : Ordinal.{u1}), Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.6643 : 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 x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.6643) (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.nfp_mul_zero Ordinal.nfp_mul_zeroₓ'. -/
@[simp]
theorem nfp_mul_zero (a : Ordinal) : nfp ((· * ·) a) 0 = 0 :=
  by
  rw [← Ordinal.le_zero, nfp_le_iff]
  intro n
  induction' n with n hn; · rfl
  rwa [iterate_succ_apply, MulZeroClass.mul_zero]
#align ordinal.nfp_mul_zero Ordinal.nfp_mul_zero

/- warning: ordinal.nfp_zero_mul -> Ordinal.nfp_zero_mul is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} (Ordinal.{u1} -> Ordinal.{u1}) (Ordinal.nfp.{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} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))))) (id.{succ (succ u1)} Ordinal.{u1})
but is expected to have type
  Eq.{succ (succ u1)} (Ordinal.{u1} -> Ordinal.{u1}) (Ordinal.nfp.{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})))) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})))) (id.{succ (succ u1)} Ordinal.{u1})
Case conversion may be inaccurate. Consider using '#align ordinal.nfp_zero_mul Ordinal.nfp_zero_mulₓ'. -/
@[simp]
theorem nfp_zero_mul : nfp ((· * ·) 0) = id :=
  by
  rw [← sup_iterate_eq_nfp]
  refine' funext fun a => (sup_le fun n => _).antisymm (le_sup (fun n => ((· * ·) 0^[n]) a) 0)
  induction' n with n hn; · rfl
  rw [Function.iterate_succ']
  change 0 * _ ≤ a
  rw [MulZeroClass.zero_mul]
  exact Ordinal.zero_le a
#align ordinal.nfp_zero_mul Ordinal.nfp_zero_mul

/- warning: ordinal.deriv_mul_zero -> Ordinal.deriv_mul_zero is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} (Ordinal.{u1} -> Ordinal.{u1}) (Ordinal.deriv.{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} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))))) (id.{succ (succ u1)} Ordinal.{u1})
but is expected to have type
  Eq.{succ (succ u1)} (Ordinal.{u1} -> Ordinal.{u1}) (Ordinal.deriv.{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})))) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})))) (id.{succ (succ u1)} Ordinal.{u1})
Case conversion may be inaccurate. Consider using '#align ordinal.deriv_mul_zero Ordinal.deriv_mul_zeroₓ'. -/
@[simp]
theorem deriv_mul_zero : deriv ((· * ·) 0) = id :=
  deriv_eq_id_of_nfp_eq_id nfp_zero_mul
#align ordinal.deriv_mul_zero Ordinal.deriv_mul_zero

/- warning: ordinal.nfp_mul_eq_opow_omega -> Ordinal.nfp_mul_eq_opow_omega 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}))) b) -> (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{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}) a Ordinal.omega.{u1})) -> (Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{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}) a Ordinal.omega.{u1}))
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})) b) -> (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{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}) a Ordinal.omega.{u1})) -> (Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.6959 : 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 x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.6959) 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}) a Ordinal.omega.{u1}))
Case conversion may be inaccurate. Consider using '#align ordinal.nfp_mul_eq_opow_omega Ordinal.nfp_mul_eq_opow_omegaₓ'. -/
theorem nfp_mul_eq_opow_omega {a b : Ordinal} (hb : 0 < b) (hba : b ≤ (a^omega)) :
    nfp ((· * ·) a) b = (a^omega.{u}) :=
  by
  cases' eq_zero_or_pos a with ha ha
  · rw [ha, zero_opow omega_ne_zero] at *
    rw [Ordinal.le_zero.1 hba, nfp_zero_mul]
    rfl
  apply le_antisymm
  · apply nfp_le_fp (mul_is_normal ha).Monotone hba
    rw [← opow_one_add, one_add_omega]
  rw [← nfp_mul_one ha]
  exact nfp_monotone (mul_is_normal ha).Monotone (one_le_iff_pos.2 hb)
#align ordinal.nfp_mul_eq_opow_omega Ordinal.nfp_mul_eq_opow_omega

/- warning: ordinal.eq_zero_or_opow_omega_le_of_mul_eq_right -> Ordinal.eq_zero_or_opow_omega_le_of_mul_eq_right is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{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 b) b) -> (Or (Eq.{succ (succ u1)} Ordinal.{u1} b (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1})))) (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}) a Ordinal.omega.{u1}) b))
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{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 b) b) -> (Or (Eq.{succ (succ u1)} Ordinal.{u1} b (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1}))) (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}) a Ordinal.omega.{u1}) b))
Case conversion may be inaccurate. Consider using '#align ordinal.eq_zero_or_opow_omega_le_of_mul_eq_right Ordinal.eq_zero_or_opow_omega_le_of_mul_eq_rightₓ'. -/
theorem eq_zero_or_opow_omega_le_of_mul_eq_right {a b : Ordinal} (hab : a * b = b) :
    b = 0 ∨ (a^omega.{u}) ≤ b :=
  by
  cases' eq_zero_or_pos a with ha ha
  · rw [ha, zero_opow omega_ne_zero]
    exact Or.inr (Ordinal.zero_le b)
  rw [or_iff_not_imp_left]
  intro hb
  change b ≠ 0 at hb
  rw [← nfp_mul_one ha]
  rw [← one_le_iff_ne_zero] at hb
  exact nfp_le_fp (mul_is_normal ha).Monotone hb (le_of_eq hab)
#align ordinal.eq_zero_or_opow_omega_le_of_mul_eq_right Ordinal.eq_zero_or_opow_omega_le_of_mul_eq_right

/- warning: ordinal.mul_eq_right_iff_opow_omega_dvd -> Ordinal.mul_eq_right_iff_opow_omega_dvd is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, Iff (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) (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}))) (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}) b)
but is expected to have type
  forall {a : Ordinal.{u1}} {b : Ordinal.{u1}}, Iff (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) (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}))) (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}) b)
Case conversion may be inaccurate. Consider using '#align ordinal.mul_eq_right_iff_opow_omega_dvd Ordinal.mul_eq_right_iff_opow_omega_dvdₓ'. -/
theorem mul_eq_right_iff_opow_omega_dvd {a b : Ordinal} : a * b = b ↔ (a^omega) ∣ b :=
  by
  cases' eq_zero_or_pos a with ha ha
  · rw [ha, MulZeroClass.zero_mul, zero_opow omega_ne_zero, zero_dvd_iff]
    exact eq_comm
  refine' ⟨fun hab => _, fun h => _⟩
  · rw [dvd_iff_mod_eq_zero]
    rw [← div_add_mod b (a^omega), mul_add, ← mul_assoc, ← opow_one_add, one_add_omega,
      add_left_cancel] at hab
    cases' eq_zero_or_opow_omega_le_of_mul_eq_right hab with hab hab
    · exact hab
    refine' (not_lt_of_le hab (mod_lt b (opow_ne_zero omega _))).elim
    rwa [← Ordinal.pos_iff_ne_zero]
  cases' h with c hc
  rw [hc, ← mul_assoc, ← opow_one_add, one_add_omega]
#align ordinal.mul_eq_right_iff_opow_omega_dvd Ordinal.mul_eq_right_iff_opow_omega_dvd

/- warning: ordinal.mul_le_right_iff_opow_omega_dvd -> Ordinal.mul_le_right_iff_opow_omega_dvd 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) -> (Iff (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{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) b) (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}))) (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}) 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) -> (Iff (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{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) b) (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}))) (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}) b))
Case conversion may be inaccurate. Consider using '#align ordinal.mul_le_right_iff_opow_omega_dvd Ordinal.mul_le_right_iff_opow_omega_dvdₓ'. -/
theorem mul_le_right_iff_opow_omega_dvd {a b : Ordinal} (ha : 0 < a) : a * b ≤ b ↔ (a^omega) ∣ b :=
  by
  rw [← mul_eq_right_iff_opow_omega_dvd]
  exact (mul_is_normal ha).le_iff_eq
#align ordinal.mul_le_right_iff_opow_omega_dvd Ordinal.mul_le_right_iff_opow_omega_dvd

/- warning: ordinal.nfp_mul_opow_omega_add -> Ordinal.nfp_mul_opow_omega_add is a dubious translation:
lean 3 declaration is
  forall {a : Ordinal.{u1}} {c : 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})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (OfNat.mk.{succ u1} Ordinal.{u1} 0 (Zero.zero.{succ u1} Ordinal.{u1} Ordinal.hasZero.{u1}))) c) -> (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) c (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})) -> (Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{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) (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})))) (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}) b) c)) (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}) a Ordinal.omega.{u1}) (Order.succ.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1}) Ordinal.succOrder.{u1} b)))
but is expected to have type
  forall {a : Ordinal.{u1}} {c : 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})) (OfNat.ofNat.{succ u1} Ordinal.{u1} 0 (Zero.toOfNat0.{succ u1} Ordinal.{u1} Ordinal.zero.{u1})) c) -> (LE.le.{succ u1} Ordinal.{u1} (Preorder.toLE.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1})) c (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})) -> (Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.nfp.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.7658 : 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 x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.7658) (HAdd.hAdd.{succ u1, succ u1, succ u1} Ordinal.{u1} Ordinal.{u1} Ordinal.{u1} (instHAdd.{succ u1} Ordinal.{u1} Ordinal.add.{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})))) (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}) b) c)) (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})))) (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}) (Order.succ.{succ u1} Ordinal.{u1} (PartialOrder.toPreorder.{succ u1} Ordinal.{u1} Ordinal.partialOrder.{u1}) Ordinal.succOrder.{u1} b)))
Case conversion may be inaccurate. Consider using '#align ordinal.nfp_mul_opow_omega_add Ordinal.nfp_mul_opow_omega_addₓ'. -/
theorem nfp_mul_opow_omega_add {a c : Ordinal} (b) (ha : 0 < a) (hc : 0 < c) (hca : c ≤ (a^omega)) :
    nfp ((· * ·) a) ((a^omega) * b + c) = (a^omega.{u}) * succ b :=
  by
  apply le_antisymm
  · apply nfp_le_fp (mul_is_normal ha).Monotone
    · rw [mul_succ]
      apply add_le_add_left hca
    · rw [← mul_assoc, ← opow_one_add, one_add_omega]
  · cases' mul_eq_right_iff_opow_omega_dvd.1 ((mul_is_normal ha).nfp_fp ((a^omega) * b + c)) with
      d hd
    rw [hd]
    apply mul_le_mul_left'
    have := le_nfp (Mul.mul a) ((a^omega) * b + c)
    rw [hd] at this
    have := (add_lt_add_left hc ((a^omega) * b)).trans_le this
    rw [add_zero, mul_lt_mul_iff_left (opow_pos omega ha)] at this
    rwa [succ_le_iff]
#align ordinal.nfp_mul_opow_omega_add Ordinal.nfp_mul_opow_omega_add

/- warning: ordinal.deriv_mul_eq_opow_omega_mul -> Ordinal.deriv_mul_eq_opow_omega_mul 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) -> (forall (b : Ordinal.{u1}), Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.deriv.{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) (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}) a Ordinal.omega.{u1}) 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) -> (forall (b : Ordinal.{u1}), Eq.{succ (succ u1)} Ordinal.{u1} (Ordinal.deriv.{u1} (fun (x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.8018 : 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 x._@.Mathlib.SetTheory.Ordinal.FixedPoint._hyg.8018) b) (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})))) (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}) b))
Case conversion may be inaccurate. Consider using '#align ordinal.deriv_mul_eq_opow_omega_mul Ordinal.deriv_mul_eq_opow_omega_mulₓ'. -/
theorem deriv_mul_eq_opow_omega_mul {a : Ordinal.{u}} (ha : 0 < a) (b) :
    deriv ((· * ·) a) b = (a^omega) * b := by
  revert b
  rw [← funext_iff,
    is_normal.eq_iff_zero_and_succ (deriv_is_normal _) (mul_is_normal (opow_pos omega ha))]
  refine' ⟨_, fun c h => _⟩
  · rw [deriv_zero, nfp_mul_zero, MulZeroClass.mul_zero]
  · rw [deriv_succ, h]
    exact nfp_mul_opow_omega_add c ha zero_lt_one (one_le_iff_pos.2 (opow_pos _ ha))
#align ordinal.deriv_mul_eq_opow_omega_mul Ordinal.deriv_mul_eq_opow_omega_mul

end Ordinal