Library sset12

Theory of Sets : Ordinals

Copyright INRIA (2009-2013) Apics; Marelle Team (Jose Grimm).

Require Import ssreflect ssrfun ssrbool eqtype ssrnat.
Require Export sset11.

Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.

Module Ordinals2.

Normal ordinal functional symbols


Definition non_coll(p: property) := (~ exists E, forall x, inc x E <-> p x).
Definition ordinal_prop (p:property) := forall x, p x -> ordinalp x.

Lemma unbounded_non_coll (p:property):
  ordinal_prop p ->
  (forall x, ordinalp x -> exists2 y, x <=o y & p y) -> non_coll p.
Proof.
move => op hp [E Ep].
have ose: ordinal_set E by move => x /Ep /op.
move:(hp _ (OS_succ (OS_sup ose))).
move => [y /ord_succ_ltP xy /Ep /(ord_sup_ub ose) h]; ord_tac.
Qed.

Lemma ordinal_not_collectivizing: non_coll ordinalp.
Proof.
apply:unbounded_non_coll => // x ox; exists x => //; ord_tac.
Qed.

Definition ordinal_interval a b := Zo b (fun z => a <=o z).

Lemma ointvP b:
  ordinalp b -> forall a z,
  (inc z (ordinal_interval a b) <-> (a <=o z /\ z <o b)).
Proof.
move => ob a z; split.
  move => /Zo_P [zb ab];split => //; apply /ord_ltP0;split => //; ord_tac.
by move => [az] /ord_ltP0 [pa pb pc]; apply: Zo_i.
Qed.

Lemma ointvP0 b: ordinalp b -> forall z,
 (inc z (ordinal_interval \0o b) <-> z <o b).
Proof.
move=> bb z; apply: (iff_trans (ointvP bb _ _ )).
split; first by case.
by move=> h; split => //; apply: ozero_least; ord_tac.
Qed.

Lemma ointv1 b: ordinalp b -> forall a,
  inc a (ordinal_interval \1o b) <-> (\0o <o a /\ a <o b).
Proof.
move => ob a; split.
 by move /(ointvP ob) => [/ord_ge1P wp1 wp2].
by move => [/ord_ge1P wp1 wp2]; apply /(ointvP ob).
Qed.

Lemma ointv_pr1 b: ordinalp b ->
  ordinal_interval \0o b = b.
Proof.
move=> ob; set_extens t.
  by move /(ointvP0 ob) /ord_ltP0 => [ _ _].
move => tb;apply /(ointvP0 ob) /ord_ltP0;split => //; ord_tac.
Qed.

Lemma ointv_pr2 a b z:
  inc z (ordinal_interval a b) -> ordinalp z.
Proof. move => /Zo_P [p1 p2]; ord_tac. Qed.

Lemma ointv_sup a b: a <o b ->
  \osup (ordinal_interval a b) = \osup b.
Proof.
move => ab.
have ob:ordinalp b by ord_tac.
have aux: sub (ordinal_interval a b) b.
  by move=> t /(ointvP ob) [_] / ord_ltP0 [_ _].
have osb: ordinal_set b by move=> t tb; ord_tac.
apply: (ord_sup_1cofinal) => //; split => // u ub.
have oa: ordinalp a by ord_tac.
have ou: ordinalp u by apply: osb.
case: (ord_le_to_ee oa ou) => aux1.
  exists u; last by ord_tac.
  by apply /(ointvP ob);split => //; apply /ord_ltP0.
exists a => //; apply /(ointvP ob); split => //; ord_tac.
Qed.

Lemma ointv_sup1 a b: a <o b -> limit_ordinal b ->
  \osup (ordinal_interval a b) = b.
Proof.
move=> ab lb; rewrite ointv_sup //.
move: ab lb => [[_ ob _] _].
by move /(limit_ordinal_P1 ob) => [_].
Qed.

Lemma least_ordinal5 x (p: property):
 \0o <o x -> ~(p x) ->
 let y := least_ordinal (fun z => (~ (\0o <o z -> p z))) x in
   [/\ ordinalp y, (\0o <o y), ~(p y) &
    (forall z, inc z (ordinal_interval \1o y) -> p z)].
Proof.
move=> ox px; have orx: ordinalp x by ord_tac.
pose q z := \0o <o z -> p z.
case: (p_or_not_p (q x)); first by move => qx; case: px; apply: qx.
move=> nq; move: (least_ordinal3 orx nq) => /=.
rewrite /q; set y := least_ordinal _ _.
move=> [y1 y2 y3]; split => //.
  by ex_middle h; case: y2.
move=> z /(ointvP y1) [/ord_ge1P z1 z2].
apply: ((y3 _ z2) z1).
Qed.

Definition ofs (f:fterm) := forall x, ordinalp x -> ordinalp (f x).
Definition ofsu (f:fterm) u := forall x, u <=o x -> ordinalp (f x).

Definition sincr_ofsu (f: fterm) u :=
  forall x y, u <=o x -> x <o y -> (f x) <o (f y).
Definition sincr_ofn f x :=
  forall a b, inc a x -> inc b x -> a <o b -> (Vf f a) <o (Vf f b).

Lemma ofs_sincru f u: sincr_ofsu f u -> ofsu f u.
Proof.
move=> pa x ux.
have ox: ordinalp x by ord_tac.
move: (pa _ _ ux (ord_succ_lt ox)) => pb; ord_tac.
Qed.

Lemma ofs_sincr f: sincr_ofs f -> ofs f.
Proof.
move=> pa x ox.
move: (pa _ _ (ord_succ_lt ox)) => pb; ord_tac.
Qed.

Lemma sincr_incr f: sincr_ofs f ->
   (forall x y, x <=o y -> f x <=o f y).
Proof.
move => h x y xy.
case: (equal_or_not x y) => exy; last by exact (proj1 (h _ _ (conj xy exy))).
have ox: ordinalp x by ord_tac.
move: (h _ _ (ord_succ_lt ox)) => [[ax _ _] _].
rewrite - exy; ord_tac.
Qed.

Lemma sincr_ofs_exten f1 f2:
  sincr_ofs f1 -> sincr_ofs f2 ->
  (forall x, ordinalp x -> exists2 y, ordinalp y & f1 x = f2 y) ->
  (forall x, ordinalp x -> exists2 y, ordinalp y & f2 x = f1 y) ->
  f1 =1o f2.
Proof.
move => i1 i2 pa pb x.
apply: (least_ordinal2 (p:= fun z => f1 z = f2 z)) => y oy py.
move: (pa _ oy) => [a oa av]; rewrite av.
case (ord_le_to_ell oa oy); first by move => ->.
  by move => ay; move: (py _ ay); move: (proj2 (i1 _ _ ay)); rewrite av.
move: (pb _ oy) => [c ob bv]; rewrite bv.
case (ord_le_to_ell ob oy); first by move => ->.
  by move => cy; move: (py _ cy) (proj2 (i2 _ _ cy)); rewrite bv => ->.
move => lt1 lt2.
move: (i2 _ _ lt2) (i1 _ _ lt1); rewrite av bv; move => h1 [h2 _]; ord_tac.
Qed.

Definition cont_ofn f x :=
   (forall a, inc a x -> limit_ordinal a ->
      Vf f a = \osup (image_by_fun f a)).

Definition normal_function f x y:=
  [/\ function_prop f x y, sincr_ofn f x & cont_ofn f x].

Definition normal_ofs1 (f: fterm) u:=
  sincr_ofsu f u /\
  (forall X, (forall x, inc x X -> u <=o x) -> nonempty X ->
    \osup (fun_image X f) = f (\osup X)).

Definition normal_ofs2 (f:fterm) u:=
  sincr_ofsu f u /\
  (forall x, limit_ordinal x -> u <o x ->
    f x = \osup (fun_image (ordinal_interval u x) f)).

Definition normal_ofs (f:fterm):=
  sincr_ofs f /\
  (forall x, limit_ordinal x -> f x = \osup (fun_image x f)).

Lemma osum_normal a: ordinalp a -> normal_ofs (fun z => a +o z).
Proof.
move=> oa; split; first by move=> x y xy; apply: osum_Meqlt.
move=> b lb; move: (lb) => [ob iob olbp].
have op: ordinalp (a +o b) by fprops.
have osE: ordinal_set (fun_image b [eta ord_sum2 a]).
  move => x /funI_P [c /(ordinal_hi ob) oc -> ]; fprops.
apply:ord_leA; last first.
  apply:ord_ub_sup => // x /funI_P [z /(ord_ltP ob) xx ->].
  by move: (proj1 (osum_Meqlt xx oa)).
red; split; [exact | by apply:OS_sup | move => w].
rewrite (osum_rec_def oa ob) => h; apply /setU_P.
case /setU2_P:h.
  by move => wa; exists a => //; apply /funI_P;ex_tac; rewrite osum0r.
move /funI_P => [z za zb]; exists (succ_o w); fprops.
move: (ordinal_hi ob za) => oz.
apply /funI_P; exists (succ_o z); [by apply: olbp | rewrite zb - osum2_succ //].
Qed.

Lemma oprod_normal a: \0o <o a -> normal_ofs (fun z => a *o z).
Proof.
move=> ap; split; first by move=> x y xy; apply: oprod_Meqlt.
have oa: ordinalp a by ord_tac.
move=> b lb; move: (lb) => [ob iob olbp].
have op: ordinalp (a *o b) by fprops.
have osE: ordinal_set (fun_image b [eta ord_prod2 a]).
  move => x /funI_P [c /(ordinal_hi ob) oc -> ]; fprops.
apply:ord_leA; last first.
  apply:ord_ub_sup => // x /funI_P [z /(ord_ltP ob) xx ->].
  by move: (proj1 (oprod_Meqlt xx ap)).
red; split; [exact | by apply:OS_sup | move => w ].
rewrite (oprod_rec_def oa ob) => /funI_P [z /setX_P [z1 z2 z3] ->].
move: (olbp _ z3) => z4.
move: (ordinal_hi ob z3) (ordinal_hi ob z4) => o3 o4.
apply /setU_P; exists (a *o (succ_o (Q z))).
  apply /(ord_ltP (OS_prod2 oa o4)); rewrite (oprod2_succ oa o3).
  apply: (osum_Meqlt _ (OS_prod2 oa o3)); ord_tac.
apply/funI_P; ex_tac.
Qed.

Lemma ord_sincr_cont_propu f u:
    (forall x, u <=o x -> f x <o f (succ_o x)) ->
    (forall x, limit_ordinal x -> u <o x ->
      f x = \osup (fun_image (ordinal_interval u x) f)) ->
    sincr_ofsu f u.
Proof.
rewrite /sincr_ofsu;move => h hc x y ux xy.
pose p y := x <o y -> f x <o f y.
apply: (least_ordinal2 (p:= p)) (proj32_1 xy) xy => z sa sc.
case: (limit_ordinal_pr2 sa).
- move => -> ha; case: (ord_lt0 ha).
- move=> [w ow wz]; rewrite wz; move /ord_lt_succP => xw.
  have hx:= (h _ (ord_leT ux xw)).
  case: (equal_or_not x w); [by move => -> | move => xnw ].
  have: w <o z by rewrite wz;apply: ord_succ_lt; apply: OS_succr; ue.
  move /sc => pw; exact: (ord_lt_ltT (pw (conj xw xnw)) hx).
- move => lz xz.
  rewrite (hc z lz (ord_le_ltT ux xz)).
  move /limit_ordinal_P3: lz => [ _ lz].
  move: (lz _ xz) => pa; move: (sc _ pa) => psx.
  move: (ord_succ_lt (proj32 ux)) => hb.
  move: (psx hb) => ha; apply:(ord_lt_leT ha).
  set E := fun_image _ _.
  have: ordinal_set E.
    move => t /funI_P [a /(ointvP sa) [ua az] ->].
    exact: (proj31_1 (h _ ua)).
  move /ord_sup_ub; apply; apply /funI_P; exists (succ_o x) => //.
  apply/ointvP => //; split =>//; apply: (ord_leT ux (proj1 hb)).
Qed.

Lemma ord_sincr_cont_prop f:
    (forall x, ordinalp x -> f x <o f (succ_o x)) ->
    (forall x, limit_ordinal x -> f x = \osup (fun_image x f)) ->
    sincr_ofs f.
Proof.
move => pa pb.
have pa1: (forall x, \0o <=o x -> f x <o f (succ_o x)).
  by move => x [_ ox _]; apply: pa.
have pa2: (forall x, limit_ordinal x ->
     \0o <o x -> f x = union (fun_image (ordinal_interval \0o x) f)).
  move => x lx; move: (lx) => /pb -> _; rewrite ointv_pr1 //;exact:(proj31 lx).
move => x y xy.
exact: (ord_sincr_cont_propu pa1 pa2 (ozero_least (proj31_1 xy)) xy).
Qed.

Lemma ord_sincr_cont_propv f x y: limit_ordinal x -> ordinalp y ->
    function_prop f x y ->
    (forall a, inc a x -> Vf f a <o Vf f (succ_o a)) ->
    cont_ofn f x ->
    normal_function f x y.
Proof.
move => [ox _ lx] oy fpxy pa pb; split => //.
move => a b ax bx ab.
pose p b := a <o b -> inc b x -> Vf f a <o Vf f b.
apply: (least_ordinal2 (p:=p)) (proj32_1 ab) ab bx => z sa sc.
case: (limit_ordinal_pr2 sa).
- move => -> ha; case: (ord_lt0 ha).
- move=> [w ow wz]; rewrite wz; move /ord_lt_succP => xw zx.
  have wx: inc w x by apply: (ordinal_transitive ox zx); fprops.
  have hx:= pa _ wx.
  case: (equal_or_not a w); [by move => -> | move => xnw ].
  have: w <o z by rewrite wz;apply: ord_succ_lt; apply: OS_succr; ue.
  move /sc => pw; exact: (ord_lt_ltT (pw (conj xw xnw) wx) hx).
- move => lz az zx; move: (lz) => [oz _ lz1].
  move: (proj31_1 az) => oa;move: (ord_succ_lt oa) => soa.
  rewrite (pb z zx lz).
  move /limit_ordinal_P3: lz=> [_ lz].
  move: (lz _ az) => pc; apply: (ord_lt_leT (sc _ pc soa (lx _ ax))).
  move: fpxy => [fx sf tf].
  have zz: sub z (source f) by rewrite sf; apply: (ordinal_transitive ox zx).
  apply:ord_sup_ub.
     move => t /(Vf_image_P fx zz) [u uz ->].
     apply: (ordinal_hi oy); rewrite - tf; Wtac.
  apply /(Vf_image_P fx zz); exists (succ_o a) => //; ord_tac.
Qed.

Lemma normal_ofs_equiv f u:
  normal_ofs1 f u <-> normal_ofs2 f u.
Proof.
rewrite /normal_ofs1/ normal_ofs2.
split; move=> [p2 p3]; split => //.
   move=> x lx ux.
   have bx: ordinalp x by ord_tac.
   set X := (ordinal_interval u x).
   have p4: forall x, inc x X -> u <=o x by move=> y /(ointvP bx) [].
   have p5: nonempty X.
     have ou: ordinalp u by ord_tac.
     exists u; apply /ointvP => //; split => //;ord_tac.
   rewrite (p3 _ p4 p5 ) (ointv_sup1 ux lx) //.
move=> X Xp neX.
move: (ofs_sincru p2) => p1.
set Y:= (fun_image X f).
have p4: ordinal_set Y by move=> i /funI_P [z zX ->]; apply: p1; apply: Xp.
have p7: (forall i, inc i X -> ordinalp i).
   by move=>j jX; move: (Xp _ jX)=> [_ ok _].
case: (inc_or_not (union X) X) => sX.
  have sf: inc (f (union X)) Y by apply /funI_P; ex_tac.
  move: (ord_sup_ub p4 sf) => le1.
  have p5: ordinalp (f (union X)) by apply: p1; apply: Xp.
  have p6: (forall i, inc i Y -> i <=o (f (union X))).
    move=> i /funI_P [z zX ->].
    move: (ord_sup_ub p7 zX) => h.
    case: (equal_or_not z (union X)) => h1; first by rewrite h1; ord_tac.
    have h2: z <o (union X) by split.
    by move: (p2 _ _ (Xp _ zX) h2) => [ok _].
  move:(ord_ub_sup p4 p5 p6) => le2; ord_tac.
have aoX: (forall x, inc x X-> ordinalp x).
  by move=> x xX; move: (Xp _ xX);move => [_ ok _ ].
case: (ord_sup_inVlimit aoX neX) => //.
move=> ls.
have [x xX [leux neux]]: exists2 x, inc x X & u <o x.
  move: neX => [y yX]; case: (equal_or_not u y); last first.
    by move => uy; ex_tac; split => //; apply: Xp.
  case: (equal_or_not X (singleton u)) => Xu.
    move: sX; rewrite {1} Xu setU_1 Xu; case; fprops.
  move => h1; rewrite - h1 in yX.
  ex_middle ba; case: Xu; apply: set1_pr => //.
  by move => z zx; ex_middle zu;case:ba;ex_tac; split;[apply: Xp| apply:nesym].
move: (ord_sup_ub aoX xX) => le1.
have ltus: u <o (union X).
  split; [ord_tac | dneg uns; rewrite -uns in le1;ord_tac ].
rewrite (p3 _ ls ltus).
move: (le1) => [_ oos _].
have sXoi: sub X (ordinal_interval u (union X)).
  move=> t tX; apply /(ointvP oos).
  by split; [ apply: Xp | apply: ord_sup_sub].
set Z:= (fun_image _ f).
have sFxoi: sub (fun_image X f) Z.
  by move => t /funI_P [z zX fz]; apply/funI_P; exists z => //; apply: sXoi.
have Zp: (forall z, inc z Z -> ordinalp z).
  by move=> z /funI_P [v /(ointvP oos) [uv _] ->]; apply: p1.
move: (ord_sup_M sFxoi Zp) => h1.
apply: ord_leA => //.
apply: ord_ub_sup => //.
  by apply: OS_sup; move=> i ifi; apply: Zp; apply: sFxoi.
have aux1: ordinalp (union X) by apply: OS_sup.
move=> i /funI_P [z /(ointvP oos) [uz zsX] ->].
have [t tX zt]: exists2 t, inc t X & z <=o t.
  ex_middle h.
  have oz: ordinalp z by move: uz => [_ ok _].
  suff: sub X z by move /(ord_ub_sup1 oz) => h3; ord_tac.
  move=> v vX.
  case: (ord_le_to_el oz (aoX _ vX)) => h3; [case: h; ex_tac | ord_tac ].
have aux: (f t) <=o (union Y).
  apply: ord_sup_ub => //; apply /funI_P; ex_tac.
case: (equal_or_not z t) => ezt; first by ue.
have lzt: z <o t by split.
move: (p2 _ _ uz lzt)=> [aux2 _]; ord_tac.
Qed.

Lemma normal_ofs_equiv1 f:
  normal_ofs1 f \0o <-> normal_ofs f.
Proof.
apply:(iff_trans (normal_ofs_equiv f _)); rewrite /normal_ofs2 /normal_ofs.
split; move=> [h2 h3]; split => //.
- move=> x y xy; apply: h2=> //; apply: ozero_least.
  by move: xy=> [[ox _] _].
- move=> x [pa pb pc].
  rewrite - {2} (ointv_pr1 pa).
  apply: h3 => //; apply /ord_ltP0;split;fprops.
- by move=> x y r1 r2; apply: h2.
- move => x lx wnz.
  have ox: ordinalp x by move: lx =>[ok _].
  by rewrite (ointv_pr1 ox); apply: h3.
Qed.

Lemma normal_ofs_equiv2 f a:
  ordinalp a -> normal_ofs f -> normal_ofs1 f a.
Proof.
move=> oa; move /normal_ofs_equiv1 => [p2 p3]; split => //.
   move=> x y xp yp; apply: p2 =>//; apply: ozero_least; ord_tac.
move=> X xp xX; rewrite p3 //.
move=> t tx; move: (xp _ tx)=> h; apply: ozero_least; ord_tac.
Qed.

Lemma normal_function_incr f a b:
  ordinalp a -> ordinalp b -> normal_function f a b ->
  (forall u v, u <=o v -> v <o a -> Vf f u <=o Vf f v).
Proof.
move => oa ob [[ff sf tf] pa pb] u v uv va.
have ua: u <o a by ord_tac.
have iua: inc u a by ord_tac.
have iva: inc v a by ord_tac.
case: (equal_or_not u v) => nuv.
  have /(ordinal_hi ob) ov : inc (Vf f v) b by Wtac.
  rewrite nuv; ord_tac.
by move:(pa _ _ iua iva (conj uv nuv)) => [].
Qed.

Lemma normal_function_equiv f a b X:
  ordinalp a -> ordinalp b -> normal_function f a b ->
  sub X a -> nonempty X ->
  (\osup X = a \/ Vf f (\osup X) = \osup (image_by_fun f X)).
Proof.
move => oa ob h.
move: (normal_function_incr oa ob h) => incf.
move: h => [[ff sf tf] pa pb] Xa nex.
set y := \osup X.
case: (equal_or_not y a) => eya; [by left | right].
have osx: ordinal_set X by move => t /Xa ta; ord_tac0.
have lya: y <o a by split => //; apply: ord_ub_sup => // x /Xa H; ord_tac.
have iya: inc y a by ord_tac.
have ofy: ordinalp (Vf f y) by apply: (ordinal_hi ob); Wtac.
have Xsf: sub X (source f) by ue.
have osi: ordinal_set (image_by_fun f X).
  move =>t /(Vf_image_P ff Xsf) [u uX ->];apply: (ordinal_hi ob); Wtac.
case: (inc_or_not y X) => iyX.
apply: ord_leA; last first.
  apply:ord_ub_sup => // t /(Vf_image_P ff Xsf) [u uX ->].
  by apply: incf => //; apply: ord_sup_ub.
  apply:ord_sup_ub => //; apply /(Vf_image_P ff Xsf); ex_tac.
case: (ord_sup_inVlimit osx nex); rewrite -/y => // limy.
rewrite (pb _ iya limy).
have ysf: sub y (source f) by rewrite sf; move: lya => [[_ _ h] _].
have Xy: sub X y by move => t tX; move:(ord_sup_sub osx iyX tX) =>h; ord_tac.
symmetry; apply: ord_sup_1cofinal; first split.
- move => t /(Vf_image_P ff Xsf) [u /Xy ux ->]; apply/(Vf_image_P ff ysf).
  ex_tac.
- move => t /(Vf_image_P ff ysf) [u /(ord_ltP (proj31 limy)) uy ->].
  move: (ord_lt_sup osx uy) => [z zx uz].
  have h: inc (Vf f z) (image_by_fun f X) by apply/(Vf_image_P ff Xsf); ex_tac.
  move: (Xa _ zx) => za.
  ex_tac; apply: incf; ord_tac.
- move =>t /(Vf_image_P ff ysf) [u uX ->];apply: (ordinal_hi ob); Wtac.
Qed.

Lemma normal_ofs_uniqueness1 f g (p:fterm) u:
   normal_ofs1 f u -> normal_ofs1 g u -> ordinalp u ->
   (forall x, u <=o x -> f (succ_o x) = p (f x)) ->
   (forall x, u <=o x -> g (succ_o x) = p (g x)) ->
   (f u = g u) ->
  (forall x, u <=o x -> f x = g x).
Proof.
move => [pa pb] [pc pd] ou pe pf pg x cp.
have ox: ordinalp x by ord_tac.
apply: (least_ordinal2 (p:= fun z => u <=o z -> f z = g z)) ox cp => y p1 p3 yp.
case: (equal_or_not u y); [by move <- | move => uny].
have uy: u <o y by [].
case: (limit_ordinal_pr2 p1).
- by move => y0; case: (@ord_lt0 u); rewrite - y0.
- move => [t ot st]; rewrite st; rewrite st in p1.
  have h : u <=o t by apply /ord_lt_succP; rewrite - st.
  move: (ord_succ_lt (proj32 h)); rewrite - {1} st => ty.
  by rewrite (pe _ h) (pf _ h) (p3 _ ty h).
- move => ly.
  set E := ordinal_interval u y.
  have pe1: (forall x, inc x E -> u <=o x) by move => t/(ointvP p1) [].
  have pe2: nonempty E by exists u; apply /(ointvP p1); split => //; ord_tac.
  have pe3: \osup E = y by rewrite (ointv_sup1 uy ly).
  move: (pb _ pe1 pe2) (pd _ pe1 pe2); rewrite pe3 => <- <-.
  apply:ord_sup_2funI => s /(ointvP p1) [s1 s2]; apply: p3 => //; ord_tac1.
Qed.

Lemma normal_ofs_uniqueness f g (p:fterm):
   normal_ofs f -> normal_ofs g ->
   (forall x, ordinalp x -> f (succ_o x) = p (f x)) ->
   (forall x, ordinalp x -> g (succ_o x) = p (g x)) ->
  (f \0o = g \0o) ->
  f =1o g.
Proof.
move => pa pb pc pd pe y oy.
have pa':= (normal_ofs_equiv2 OS0 pa).
have pb':= (normal_ofs_equiv2 OS0 pb).
have pc':(forall x, \0o <=o x -> f (succ_o x) = p (f x)).
  move => x xp; apply: pc; ord_tac.
have pd':(forall x, \0o <=o x -> g (succ_o x) = p (g x)).
  move => x xp; apply: pd; ord_tac.
apply:(normal_ofs_uniqueness1 pa' pb' OS0 pc' pd' pe); ord_tac1.
Qed.

Lemma normal_ofs_existence (p:fterm) a
  (osup := fun y f => \osup (fun_image y (fun z => (p (f z)))))
  (osupp:= fun f => Yo (source f = \0o) a (osup (source f) (Vf f)))
  (f:= fun x => Vf (transfinite_defined (ordinal_o (succ_o x)) osupp) x):
  (forall x, ordinalp x -> x <o p x) ->
  (forall x y, x <=o y -> p x <=o p y) ->
  ordinalp a ->
  [/\ normal_ofs f, f \0o = a &
   (forall x, ordinalp x -> f (succ_o x) = p (f x)) ].
Proof.
move => incrp pi oa.
pose aux b := transfinite_defined (ordinal_o b) osupp.
have frec: forall b, ordinalp b ->
     [/\ function (aux b), source (aux b) = b,
       (inc \0o b -> Vf (aux b) \0o = a) &
       (forall y, inc y b -> y <> \0o ->
         Vf (aux b) y = osup y (Vf (aux b)))].
  move=> b ob.
  move: (transfinite_defined_pr osupp (ordinal_o_wor ob)).
  rewrite /transfinite_def (ordinal_o_sr b) //.
  have sh: forall c, inc c b -> source
    (restriction_to_segment (ordinal_o b) c
      (transfinite_defined (ordinal_o b) osupp)) = c.
    move=> c cb; rewrite /restriction_to_segment /restriction1; aw.
    apply: ordinal_segment => //.
  move=> [[h1 _] h2 h3]; split => //.
    by move=> zb;rewrite (h3 _ zb); rewrite /osupp Y_true //; apply: sh.
  move => y xb l4; rewrite (h3 _ xb); move: (sh _ xb) => sh1.
  rewrite /osupp /osup sh1; Ytac0; congr union.
  have oy: ordinalp y by ord_tac.
  have aux1: forall z, inc z y ->
   Vf (aux b) z = Vf (restriction_to_segment (ordinal_o b) y
              (transfinite_defined (ordinal_o b) osupp)) z.
    move=> z zy.
    have lt1: glt (ordinal_o b) z y.
      apply /ordo_ltP => //; apply: (ordinal_transitive ob xb zy).
    rewrite restriction1_V //;last by apply /segmentP.
    rewrite h2 - {2} (ordinal_o_sr b); apply: sub_segment.
  by set_extens t; move /funI_P => [z z1 z2]; apply /funI_P;
       ex_tac; rewrite z2 (aux1 _ z1) //.
have frec2: forall b1 b2 x, ordinalp b1 -> ordinalp b2 ->
     inc x b1 -> inc x b2 -> Vf (aux b1) x = Vf (aux b2) x.
  move => b1 b2 x ob1 ob2.
  pose q z := inc z b1 -> inc z b2 -> Vf (aux b1) z = Vf (aux b2) z.
  move => xb1; move: (ordinal_hi ob1 xb1) => x1; move: xb1.
  apply:(least_ordinal2 (p:=q)) => // z oz zp2 tb1 tb2.
  move: (frec _ ob1) => [_ _ h0 hg].
  move: (frec _ ob2) => [_ _ h0' hg'].
  case: (equal_or_not z \0o) => ez0; first by rewrite ez0 h0 ?h0'//;ue.
  rewrite (hg _ tb1 ez0) (hg' _ tb2 ez0); apply: ord_sup_2funI => t tz.
  have ltwz: t <o z by apply /ord_ltP.
  have zb1: inc t b1 by exact: (ordinal_transitive ob1 tb1 tz).
  have zb2: inc t b2 by exact: (ordinal_transitive ob2 tb2 tz).
  rewrite ((zp2 t ltwz) zb1 zb2) //.
have f0: f \0o = a.
   move: (frec _ OS1) => [_ _]; rewrite - succ_o_zero -/(f _) => h _.
   apply: h; fprops.
have frec3: forall y, \0o <o y -> f y = osup y f.
  move=> y yp.
  have oy: ordinalp y by ord_tac.
  have ob: ordinalp (succ_o y) by fprops.
  have ynz: y <> \0o by move: yp => [_ h]; apply: nesym.
  have iyb: inc y (succ_o y) by rewrite /succ_o; fprops.
  move: (frec _ ob) => [_ _ h0 hg]; rewrite {1} /f (hg _ iyb ynz) /osup.
  suff A: forall t, inc t y ->
     Vf (aux (succ_o y)) t = Vf (aux (succ_o t)) t.
     congr union; set_extens t; move => /funI_P [w z1 z2]; apply /funI_P;
       ex_tac; rewrite z2 (A _ z1) //.
  move => t ty; apply: frec2 => //; [apply:OS_succ; ord_tac | | fprops].
  rewrite /succ_o; fprops.
have frec4: forall x, ordinalp x -> ordinalp (f x).
   move => x; apply:(least_ordinal2 (p:=fun z => ordinalp (f z))) => z oz zp2.
   case: (equal_or_not z \0o) => zp; first by rewrite zp f0.
   rewrite (frec3 _ (ord_ne0_pos oz zp)); apply:OS_sup.
   move => t /funI_P [u /(ord_ltP oz) /zp2 /incrp h ->]; ord_tac.
have frec5: forall x y, x <o y -> p (f x) <=o f y.
  move => x y xy.
  have oz: ordinalp y by ord_tac.
  have ixy: inc x y by apply /ord_ltP.
  have yp: \0o <o y by apply:ord_ne_pos => //; ex_tac.
  rewrite (frec3 _ yp) /osup; apply: ord_sup_ub; last by apply /funI_P; ex_tac.
  move => t /funI_P [z /(ordinal_hi oz) /frec4 /incrp h ->]; ord_tac.
have frec6: forall x y, x <o y -> (f x) <o f y.
  move => x y xy; move: (frec5 _ _ xy) => l1.
  move: (incrp _ (frec4 _ (proj31_1 xy))) => l2; ord_tac.
have frec7:forall x, ordinalp x -> f (succ_o x) = p (f x).
  move => x ox.
  have xsx: x <o succ_o x by apply: ord_succ_lt.
  apply: ord_leA; last by apply: (frec5 _ _ xsx).
  move: (OS_succ ox) => osx.
  have sxp: \0o <o succ_o x by apply /ord_lt_succP; apply: ozero_least.
  rewrite (frec3 _ sxp) /osup;apply: ord_ub_sup.
  - move => t /funI_P [z /(ordinal_hi osx) /frec4 /incrp h ->]; ord_tac.
  - move: (incrp _ (frec4 _ ox)) => h; ord_tac.
  - move => t /funI_P [z zx ->]; apply: pi.
    case/setU1_P: zx; last by move ->; move: (frec4 _ ox) =>h; ord_tac.
    by move /(ord_ltP ox) => /frec6 [].
split => //; split =>// x lx; move:(proj31 lx) => ox.
rewrite (frec3 _ (limit_positive lx)) /osup.
have hz: forall z, inc z x -> z <o succ_o z.
  move => z zx ;apply: ord_succ_lt; ord_tac.
apply: ord_sup_2cofinal;split; move => c /funI_P [z zx ->]; move:(hz _ zx)=> ht.
  move:(proj33 lx _ zx) => h; exists (f (succ_o z)); last by apply:frec5.
  apply /funI_P; ex_tac.
exists (f (succ_o z)); first by apply/funI_P; ex_tac; apply: frec7; ord_tac.
by move: (frec6 _ _ ht) => [].
Qed.

Lemma normal_ofs_limit1 f u x: normal_ofs1 f u -> u <o x -> limit_ordinal x ->
  limit_ordinal (f x).
Proof.
move=> /normal_ofs_equiv [pa pb] cux lx.
rewrite (pb _ lx cux); move/limit_ordinal_P3: (lx) => [[[_ ox _] _] pd].
have ov := (ofs_sincru pa).
set E := (fun_image (ordinal_interval u x) f).
have ou: ordinalp u by ord_tac.
have uu: inc u (ordinal_interval u x) by apply/(ointvP ox);split=> //;ord_tac.
have oe: ordinal_set E.
  by move=> t /funI_P [z /(ointvP ox) [uz _] ->]; apply: ov.
have ne: nonempty E by rewrite /E;exists (f u); apply /funI_P;ex_tac.
case: (ord_sup_inVlimit oe ne) => //.
move=> /funI_P [z /(ointvP ox) [sa /pd sb] fz].
have lt1:= (ord_succ_lt (proj32 sa)).
have se: inc (f (succ_o z)) E.
  apply /funI_P; exists (succ_o z)=> //;apply /(ointvP ox).
  split => //;exact (ord_leT sa (proj1 lt1)).
move: (ord_sup_ub oe se); rewrite fz => l1.
move: (pa z (succ_o z) sa lt1) => l2; ord_tac.
Qed.

Lemma normal_ofs_limit f x: normal_ofs f -> limit_ordinal x ->
  limit_ordinal (f x).
Proof.
move => /(normal_ofs_equiv2 OS0) nf lx; apply:(normal_ofs_limit1 nf _ lx).
by apply: limit_positive.
Qed.

Lemma normal_ofs_compose1 f fb g gb:
 ordinalp fb -> ordinalp gb -> fb <=o g gb ->
 normal_ofs1 f fb -> normal_ofs1 g gb -> normal_ofs1 (f \o g) gb.
Proof.
move => ofb ogb le1 [sa pa] [sb pb].
have aux: forall z, gb <=o z -> fb <=o g z.
  move => z zh; apply: (ord_leT le1).
  move: (ofs_sincru sb zh)=> oug.
  case: (equal_or_not gb z); [move ->;ord_tac | move =>h].
  exact (proj1 (sb gb z (ord_leR ogb) (conj zh h))).
split.
  move => u v ha uv /=.
  by move: (sb _ _ ha uv); apply: sa; apply: aux.
move => X Xp neX /=; rewrite - (pb _ Xp neX).
set Y := (fun_image X g).
have sc:(forall x, inc x Y -> fb <=o x).
  by move => x /funI_P [z /Xp zx ->]; apply: aux.
have nY: nonempty Y by move: neX => [t tX]; exists (g t); apply /funI_P; ex_tac.
rewrite -(pa _ sc nY); congr union; set_extens t; move/funI_P => [z za ->].
  apply/funI_P; exists (g z)=> //;apply /funI_P; ex_tac.
move/funI_P: za => [u tX ->]; apply/funI_P; ex_tac.
Qed.

Lemma normal_ofs_compose f g:
 normal_ofs f -> normal_ofs g -> normal_ofs (f \o g).
Proof.
move => /(normal_ofs_equiv2 OS0) pa /(normal_ofs_equiv2 OS0) pb.
have h := ozero_least (ofs_sincru (proj1 pb) (ord_leR OS0)).
by move:(normal_ofs_compose1 OS0 OS0 h pa pb) => /normal_ofs_equiv1.
Qed.

Lemma osum_limit x y: ordinalp x -> limit_ordinal y ->
  limit_ordinal (x +o y).
Proof.
by move=> ox ly; apply:normal_ofs_limit=> //; apply: osum_normal.
Qed.

Lemma oprod_limit x y: \0o <o x -> limit_ordinal y ->
  limit_ordinal (x *o y).
Proof. by move=> ox ly; apply:normal_ofs_limit=> //; apply: oprod_normal. Qed.

Lemma osi_gex x f: sincr_ofs f -> ordinalp x -> x <=o (f x).
Proof.
move=> finc ox.
case: (ord_le_to_el ox (ofs_sincr finc ox)) => // lt1.
move: (least_ordinal4 ox lt1 (p:=fun z => f z <o z)) => [pd pb pc].
move: (finc _ _ pb) => ha.
move: (pc _ (proj32_1 ha) ha) => lt2; ord_tac.
Qed.

Lemma odiff_normal a: ordinalp a -> normal_ofs1 (ord_diff ^~ a) a.
Proof.
move => oa; apply/normal_ofs_equiv.
have aux1:forall u v, a <=o u -> u <o v -> u -o a <o v -o a.
  move => u v au uv; move: (ord_leT au (proj1 uv)) => av.
  move: (odiff_pr au) (odiff_pr av) => [ou e1] [ov e2].
  by move: uv; rewrite {1} e1 {1} e2; move /(osum_Meqltr ou ov oa).
have aux:forall u v, a <=o u -> u <=o v -> u -o a <=o v -o a.
  move => u v au uv; move: (ord_leT au uv) => av.
  move: (odiff_pr au) (odiff_pr av) => [ou e1] [ov e2].
  by move: uv; rewrite {1} e1 {1} e2; move /(osum_Meqler ou ov oa).
split => //.
move => x lx ax.
have ox:= (proj31 lx).
set E := fun_image _ _.
have ose: ordinal_set E.
  by move => t /funI_P [v /(ointvP ox) [[_ ov _] vx] -> ]; apply: OS_diff.
have m1: ordinal_ub E (x -o a).
  by move => t /funI_P [v /(ointvP ox) [av [vx _]] -> ]; apply: aux.
move: (odiff_pr (proj1 ax)) => [sa sb].
have os:= (OS_sup ose).
move:(ord_ub_sup ose sa m1) => le1.
case: (ord_le_to_el sa os) => le2; first by ord_tac.
move: (osum_Meqlt le2 oa); rewrite - sb => eq1.
have: inc (\osup E) E.
  apply /funI_P;exists (a +o \osup E); last by symmetry; apply: odiff_pr1.
  by apply/(ointvP ox); split => //; apply:osum_Mle0.
move => /funI_P [y /(ointvP ox) [e1 e2] eq].
move/(limit_ordinal_P3): (lx) => [ _ h3]; move: (h3 _ e2) => h4.
have h5 :=(ord_succ_lt (proj32 e1)).
move: (aux1 _ _ e1 h5) => h6.
have se: (inc ((succ_o y) -o a) E).
   apply/funI_P; exists (succ_o y) => //; apply/(ointvP ox); split => //.
   move: (proj1 h5) => e3; ord_tac.
move: (ord_sup_ub ose se); rewrite eq => h7; ord_tac.
Qed.

Lemma normal_shift f a: normal_ofs f -> ordinalp a ->
  normal_ofs (fun z => (f(a +o z) -o a)).
Proof.
move => sa oa.
move: (normal_ofs_compose sa (osum_normal oa)) => sb.
move: (odiff_normal oa) => sc.
set f1 := (ord_diff^~ a); set f2:= (f \o [eta ord_sum2 a]).
have h1: a <=o f2 \0o.
 rewrite /f2 /= (osum0r oa); exact: (osi_gex (proj1 sa) oa).
move/normal_ofs_equiv1: sb => sb'; apply/normal_ofs_equiv1.
exact:(normal_ofs_compose1 oa OS0 h1 sc sb').
Qed.

Lemma osi_gexu f u t x:
  sincr_ofsu f u -> u <=o t -> t <=o f t -> t <=o x ->
   x <=o (f x).
Proof.
move=> finc ut tft tx.
have ox: ordinalp x by ord_tac.
case: (ord_le_to_el ox (ofs_sincru finc (ord_leT ut tx))) => // lt1.
pose p x := t <=o x /\ f x <o x.
move: (least_ordinal4 ox (conj tx lt1) (p:=p)) => [pd [pb1 pb2] pc].
set y := least_ordinal p x.
case: (equal_or_not t y) => ty.
  by move: pb2; rewrite -/y - ty => h; ord_tac.
move: (ord_leT tft (proj1 (finc t y ut (conj pb1 ty)))) => pa.
have ofy: ordinalp (f y) by ord_tac.
move: (pc _ ofy (conj pa (finc (f y) y (ord_leT ut pa) pb2))) => h; ord_tac.
Qed.

Lemma normal_fn_unbounded f a x:
  normal_function f a a -> x<o a -> x <=o (Vf f x) /\ Vf f x <o a.
Proof.
 move=> [[ff sf tf] finc _] laxx.
have oa: ordinalp a by ord_tac.
have ox: ordinalp x by ord_tac.
have xsf: inc x a by apply /(ord_ltP oa).
have ft: inc (Vf f x) a by Wtac.
split; last by apply /(ord_ltP oa).
have ofx:= (ordinal_hi oa ft).
case: (ord_le_to_el ox ofx) => // lt1.
pose p z := z <o a /\ Vf f z <o z.
have px: p x by done.
move: (least_ordinal4 ox px) => [pc [pd pe] pf].
move: (ord_le_ltT (proj1 pe) pd) => a2.
move /(ord_ltP oa): pd => a1.
move /(ord_ltP oa): (a2) => a3.
move: (finc _ _ a3 a1 pe) => ha.
have hb: p (Vf f (least_ordinal p x)) by [].
move: (pf _ (proj32_1 ha) hb) => lt2; ord_tac.
Qed.

Lemma osum_increasing5 a w: ordinalp a ->
  (sincr_ofsu w a) ->
  ((forall x y, a <=o x -> ordinalp y -> (w (x) +o y) <=o w (x +o y))
  /\ (exists2 b, a <=o b & forall x, b <=o x -> x <=o w (x))).
Proof.
move=> oa h.
have osw:= (ofs_sincru h).
have pr1: forall x y, a <=o x -> ordinalp y -> (w x +o y) <=o w (x +o y).
  move=> x y ax oy; have ox: ordinalp x by ord_tac.
  apply:(least_ordinal2 (p:= fun z => (w x +o z) <=o w (x +o z))) oy => z oz hr.
  move: (osw _ ax) => owx.
  have pa: forall t, ordinalp t -> a <=o (x +o t).
    move=> t ot;move: (osum_Mle0 ox ot) => h2; ord_tac.
  case: (limit_ordinal_pr2 oz).
  - move=> ->; rewrite (osum0r ox)(osum0r owx); ord_tac.
  - move=> [t ot uv].
    move: (ord_succ_lt ot); rewrite -uv => h1; move: (hr _ h1) => le1.
    move: (h _ _ (pa _ ot) (osum_Meqlt h1 ox)) => lt1.
    rewrite {1} uv - (osum2_succ owx ot); apply /ord_succ_ltP; ord_tac.
  - move: (osum_normal owx) => [_ opn].
    move=> lz; rewrite (opn _ lz).
    apply: ord_ub_sup.
      move => t /funI_P [tu uz ->]; apply: OS_sum2 => //; ord_tac0.
    by apply: osw; apply: pa.
  move=> i /funI_P[j jz ->].
  have ljz: j <o z by apply /ord_ltP.
  move: (hr _ ljz) => le1.
  move: (h _ _ (pa _ (proj31_1 ljz)) (osum_Meqlt ljz ox)) => [ lt1 _]; ord_tac.
split; first by exact;move=> a w oa h1.
set b:= succ_o a.
have ob: ordinalp b by apply: OS_succ.
move: (ord_succ_lt oa); rewrite -/b; move => [leab _].
set c := b *o omega0.
have ab:= (ord_leT leab (oprod_Mle1 ob ord_lt_0omega)).
exists c; first by exact.
suff bv: c <=o w c.
  move => x xc; apply: (osi_gexu h ab bv xc).
have bp: \0o <o b by apply:ord_ne_pos => //; exists a; rewrite /b; fprops.
move: (oprod_normal bp) => [_ aux].
move: (aux _ omega_limit); rewrite - /c => e1; rewrite {1} e1.
have owc: ordinalp (w c) by apply:osw.
have H: forall z, inc z omega0 -> ordinalp (b *o z).
   move => z zo;exact (OS_prod2 ob (ordinal_hi OS_omega zo)).
apply: ord_ub_sup => // t /funI_P[z zo ->]; move:(H _ zo) => obz //.
have owb: ordinalp (w b) by apply: osw.
have oz := (ordinal_hi OS_omega zo).
have le1: b *o z <=o (w b +o (b *o z)) by apply: osum_M0le.
move: (pr1 _ _ leab obz).
rewrite (osum_x_xy ob oz).
set n:= (\1o +o z) => le2.
move: (ord_leT le1 le2) => le3.
suff le4: w (b *o n) <=o w (b *o omega0) by ord_tac.
case: (equal_or_not (b *o n) (b *o omega0)) => sv.
   rewrite sv; apply: ord_leR; by apply: osw.
have [ltno _]: n <o omega0.
   move: BS1=> b1; apply/ (ord_ltP OS_omega); rewrite/n osum2_2int //; fprops.
move: (oprod_Meqle ltno ob) => le4.
have lt1:(b *o n) <o (b *o omega0) by split.
have le5: a <=o (b *o n).
  rewrite /n -(osum_x_xy ob oz); apply: (ord_leT leab).
  by apply: osum_Mle0.
by move: (h _ _ le5 lt1) => [ok _].
Qed.

Lemma osi_gex1 x f:
   sincr_ofs f -> ordinalp x -> exists y,
      [/\ ordinalp y, x <=o (f y) &
        forall z, ordinalp z -> x <=o (f z) -> y <=o z].
Proof.
move=> finc ox.
move: (osi_gex finc ox) => lxy.
pose p y := x <=o (f y).
move: (@least_ordinal4 x p ox lxy) => [pa pb pc].
exists (least_ordinal p x);split => //.
Qed.

Lemma normal_ofs_bounded x f: ordinalp x -> normal_ofs f ->
  x <o f \0o \/ exists y, [/\ ordinalp y, f y <=o x & x <o f (succ_o y)].
Proof.
move=> ox [pa pb].
move: (osi_gex1 pa ox) => [y [oy xle ymin]].
case: (equal_or_not x (f y)) => xo.
  move: (pa _ _ (ord_succ_lt oy)) => lt1.
  rewrite xo;right; exists y; split => //; apply: ord_leR; ord_tac.
have xlt: x <o f y by split.
case: (limit_ordinal_pr2 oy).
    by move => ye; move: xlt; rewrite ye => xx; left.
  move => [z oz yz]; rewrite yz in oy.
  right; exists z; rewrite -yz;split => //.
  move: (ord_succ_lt oz); rewrite -yz => le3.
  move: (pa _ _ le3) => [[ofz _] _].
  case: (ord_le_to_ee ofz ox) => //.
  move=> le1; move: (ymin _ oz le1) => le2; ord_tac.
move => li; move: xlt; rewrite (pb _ li) => xu.
have os: ordinal_set (fun_image y f).
  move=> t /funI_P [z zy ->]; move: (ordinal_hi oy zy) => oz.
  exact: (ofs_sincr pa oz).
move: (ord_lt_sup os xu) => [z /funI_P [t ty ->] le2].
have yt: t <o y by apply /(ord_ltP oy).
move:(ymin _ (ordinal_hi oy ty) (proj1 le2)) => yt'; ord_tac.
Qed.

Definition least_fixedpoint_ge f x y:=
 [/\ x <=o y, f y = y & (forall z, x <=o z -> f z = z -> y <=o z)].
Definition the_least_fixedpoint_ge f x :=
  (\osup (target (induction_defined f x))).

Definition fixpoints f := Zo (source f) (fun z => Vf f z = z).

Lemma normal_ofs_fix1 f u x:
  normal_ofs1 f u -> u <=o x -> x <=o f x ->
  least_fixedpoint_ge f x (the_least_fixedpoint_ge f x).
Proof.
move=> nf0 ux xfx; rewrite /the_least_fixedpoint_ge.
have ox: ordinalp x by ord_tac.
move: nf0 => [sif nf].
have aux: forall t, x <=o t -> t <=o f t.
  by move => t; apply:(osi_gexu sif ux xfx).
move: (induction_defined_pr f x).
set g := induction_defined f x; move=> [sg sjg gz gnz].
have fg: function g by fct_tac.
set y := \osup (target g).
have pb: forall i, inc i (target g) -> x <=o i.
   move=> i itg; move: ((proj2 sjg) _ itg) => [j jsg <-].
   move: j jsg; rewrite sg; apply: cardinal_c_induction.
     rewrite gz; ord_tac.
   move=> n nB; rewrite (gnz _ nB) => p1.
   move: (aux _ p1) => h; ord_tac.
have pc: (forall i, inc i (target g) -> u <=o i).
   move=> i itg; move: (pb _ itg)=> le2; ord_tac.
have xi: inc x (target g) by rewrite -gz; Wtac; rewrite sg; fprops.
have ne: nonempty (target g) by exists x.
have otg: ordinal_set (target g).
  move=> t tg; move: (pb _ tg) => le; ord_tac.
split => //.
- by apply:(ord_sup_ub otg xi).
- rewrite - (nf _ pc ne).
  apply: ord_sup_1cofinal; [ split | done].
    move => t /funI_P [z ztg ->].
    move: ((proj2 sjg) _ ztg) => [i isg <-].
    rewrite sg in isg; rewrite -gnz //; Wtac;rewrite sg; fprops.
  move=> a atg.
  move: ((proj2 sjg) _ atg) => [i isg <-].
  rewrite sg in isg.
  have wt: inc (Vf g i) (target g) by Wtac.
  exists (Vf g (succ i)); rewrite (gnz _ isg).
     by apply /funI_P; exists (Vf g i).
  by apply: aux; apply: pb.
- move => z xz fz.
  have oz: ordinalp z by ord_tac.
  apply: ord_ub_sup => //.
  move=> i itg; move: ((proj2 sjg) _ itg) => [j jsg <-].
  move: j jsg; rewrite sg; apply: cardinal_c_induction; first by rewrite gz.
  move=> n nB; rewrite (gnz _ nB) => p1.
  have /pc le1: inc (Vf g n) (target g) by Wtac.
  case: (equal_or_not (Vf g n) z) => h; first by rewrite h fz; ord_tac.
  rewrite - fz; exact: (proj1 (sif _ _ le1 (conj p1 h))).
Qed.

Lemma normal_ofs_fix x f:
  normal_ofs f -> ordinalp x ->
  least_fixedpoint_ge f x (the_least_fixedpoint_ge f x).
Proof.
move => nf ox.
move/normal_ofs_equiv1:(nf)=> nf1; apply: (normal_ofs_fix1 nf1).
  ord_tac1.
exact:(osi_gex (proj1 nf) ox).
Qed.

Lemma normal_function_fix f a x
  (y:= the_least_fixedpoint_ge (Vf f) x):
  normal_function f a a -> x <o a ->
  (y = a \/
  [/\ x <=o y, y <o a, Vf f y = y &
    (forall z, x <=o z -> z <o a -> Vf f z = z -> y <=o z)]).
Proof.
move=> nf lxa.
have aux: forall x, x <o a -> x <=o (Vf f x) /\ Vf f x <o a.
  by move => t ta; apply: normal_fn_unbounded.
have ox: ordinalp x by ord_tac.
have oa: ordinalp a by ord_tac.
move: (induction_defined_pr (Vf f) x).
set g := induction_defined (Vf f) x; move=> [sg sjg gz gnz].
have fg: function g by fct_tac.
move: (nf) => [[ff sf tf] sif nf0].
have pb: forall i, inc i (target g) -> x <=o i /\ i <o a.
   move=> i itg; move: ((proj2 sjg) _ itg) => [j jsg <-].
   move: j jsg; rewrite sg; apply: cardinal_c_induction.
     rewrite gz; split => //;ord_tac.
   move=> n nB; rewrite (gnz _ nB); move => [p1 p2].
   move: (aux _ p2) => [s1 s2]; split => //; ord_tac.
have osg: ordinal_set (target g) by move => i /pb [_] ia;ord_tac.
have sta: sub (target g) a by move => t /pb [_] h; ord_tac.
have netg: nonempty (target g) by exists (Vf g \0c); Wtac; rewrite sg; fprops.
have sa: x <=o y.
  apply :ord_sup_ub => //; rewrite - /g - gz; Wtac; rewrite sg; fprops.
have sb: y <=o a by apply :ord_ub_sup => // i /pb [_ []].
case: (equal_or_not y a) => eya; [by left | right;split => //; last first].
   move => z xz za fz.
   have oz: ordinalp z by ord_tac.
   apply: ord_ub_sup => //.
   move=> i itg; move: ((proj2 sjg) _ itg) => [j jsg <-].
   move: j jsg; rewrite sg; apply: cardinal_c_induction; first by rewrite gz.
   move=> n nB; rewrite (gnz _ nB) => p1; rewrite - fz.
   case: (equal_or_not ( Vf g n) z) => neq; first by rewrite neq fz; ord_tac.
   have ha: inc (Vf g n) a by apply /(ord_ltP oa); ord_tac.
   have hb: inc z a by ord_tac.
   exact (proj1(sif (Vf g n) z ha hb (conj p1 neq))).
move: (aux _ (conj sb eya)) => [sc sd].
apply: ord_leA => //.
move: (sta); rewrite - sf => stf.
have ha: ordinal_set (image_by_fun f (target g)).
  move => t /(Vf_image_P ff stf) [u /pb [_ /aux [_ h]] ->]; ord_tac.
case: (normal_function_equiv oa oa nf sta netg) => //; rewrite -/y => ->.
apply: ord_ub_sup => //; first by ord_tac.
move => t /(Vf_image_P ff stf) [u utg ->].
move: (proj2 sjg _ utg)=> [v va <- ].
rewrite sg in va; rewrite - (gnz _ va); apply :ord_sup_ub => //.
rewrite -/g; Wtac; rewrite sg; fprops.
Qed.

Lemma next_fix_point_small f C (E:= succ_c C): infinite_c C ->
  normal_ofs f ->
  (forall x, inc x E -> inc (f x) E) ->
  (forall x, inc x E -> inc (the_least_fixedpoint_ge f x) E).
Proof.
move => icE nf fee.
move: (succ_c_pr1 (proj1 icE)) => [pa pb pc].
move => x xE.
move: (induction_defined_pr f x) => [qa qb qc qd].
move: (image_smaller_cardinal (proj1 qb));rewrite qa (surjective_pr0 qb)=> sa.
apply: succ_c_sup => //.
  by apply: (card_leT sa); rewrite cardinal_Bnat; apply /infinite_c_P2.
move => t /(proj2 qb); rewrite qa; move => [z zB <-]; move: z zB.
apply: cardinal_c_induction; first by rewrite qc.
move => n nB /fee hr; rewrite qd //.
Qed.

Lemma next_fix_point_small1 f C x (E:= succ_c C)
  (y:= the_least_fixedpoint_ge (Vf f) x):
  infinite_c C -> normal_function f E E -> inc x E ->
   [/\ x <=o y, inc y E, Vf f y = y &
    (forall z, x <=o z -> inc z E -> Vf f z = z -> y <=o z)].
Proof.
move => icE nf xE.
move: (succ_c_pr1 (proj1 icE)) => [pa pb pc].
move: (induction_defined_pr (Vf f) x) => [qa qb qc qd].
move: (image_smaller_cardinal (proj1 qb));rewrite qa (surjective_pr0 qb)=> sa.
have fee: (forall x, inc x E -> inc (Vf f x) E).
  move:nf => [[ff sf tf] pd pe].
  rewrite -{1} sf - tf => t tsf; Wtac.
have ye: inc y E.
  apply: succ_c_sup => //.
    by apply: (card_leT sa); rewrite cardinal_Bnat; apply /infinite_c_P2.
  move => t /(proj2 qb); rewrite qa; move => [z zB <-]; move: z zB.
  apply: cardinal_c_induction; first by rewrite qc.
  move => n nB /fee hr; rewrite qd //.
have [oe _] := (pa).
have xe: x <o E by apply/ord_ltP=> //.
case: (normal_function_fix nf xe); rewrite -/y.
  by move => yee; move:ye; rewrite yee; move: (ordinal_irreflexive oe).
by move => [ta tb tc td]; split => // z xz ze; apply: td => //; apply/ord_ltP.
Qed.

Lemma normal_fix_cofinal C (E := succ_c C) f:
   infinite_c C -> normal_function f E E ->
   ord_cofinal (fixpoints f) E.
Proof.
move => icE nf.
move: (nf) => [[ff sf tf] _ _].
split; first by rewrite - sf; apply: Zo_S.
move => x xE.
move: (next_fix_point_small1 icE nf xE); rewrite -/E - sf.
set y := the_least_fixedpoint_ge (Vf f) x; move => [pa pb pc pd].
by exists y => //; apply/Zo_P.
Qed.

Lemma big_ofs
  (p:= fun z => succ_c (cardinal z))
  (osup := fun y f => \osup (fun_image y (fun z => (p (f z)))))
  (osupp:= fun f => Yo (source f = \0o) \0o (osup (source f) (Vf f)))
  (f:= fun x => Vf (transfinite_defined (ordinal_o (succ_o x)) osupp) x):
  [/\ normal_ofs f, f \0o = \0o,
   (forall x, ordinalp x -> f (succ_o x) = p (f x))&
   forall C, infinite_c C ->
       exists x, inc x (succ_c C) /\ ~ (inc (f x) (succ_c C)) ].
Proof.
have sa: (forall x, ordinalp x -> x <o p x).
  move => x ox; rewrite /p.
  have pc: cardinalp (cardinal x) by fprops.
  have pa: cardinalp (succ_c (cardinal x)) by apply: CS_succ_c.
  apply /(ord_ltP (proj1 pa)); apply /(succ_cP pc); split; fprops.
have sb: (forall x y : Set, x <=o y -> p x <=o p y).
  move => x y xy; rewrite /p.
  move:(ordinal_cardinal_le1 xy) => h.
  by apply:ordinal_cardinal_le; apply:succ_c_pr6.
move: (normal_ofs_existence sa sb OS0).
rewrite -/osup -/osupp -/f.
move => [pa pb pc]; split => //.
move => c ic; exists (succ_o c).
move: (proj1 ic) => cc; move: (proj1 cc) => oc.
move: (CS_succ_c cc)=> cs.
have oso: ordinalp (succ_o c) by move: cc=> [co _];apply:OS_succ.
split.
   have os1 := OS1.
   apply /(succ_cP cc); split => //.
   rewrite - ord_succ_pr // osum_cardinal // (card_card cc) sum2_infinite //.
     fprops.
   rewrite (card_card CS1); apply: finite_le_infinite => //; fprops.
have eq1: (f (succ_o c)) = p (f c) by apply: pc.
move /(succ_cP cc)=> [sc sd].
have se: c <=o (f c) by apply: (osi_gex (proj1 pa) oc).
have: p c <=o p (f c) by apply: sb.
move /ordinal_cardinal_le1; rewrite - eq1 /p (card_card cc).
rewrite (card_card cs) => h.
move: (card_leT h sd); move: (succ_c_pr2 cc) => ra rb; co_tac.
Qed.

Definition omax x y:= Yo (x <=o y) y x.

Lemma OS_omax x y: ordinalp x -> ordinalp y -> ordinalp(omax x y).
Proof. by move => ox oy; rewrite /omax;Ytac w. Qed.

Lemma omaxC x y: ordinalp x -> ordinalp y -> omax x y = omax y x.
Proof.
rewrite /omax => ox oy.
case: (ord_le_to_ell ox oy).
+ by move: (ord_leR oy) =>h; move => ->; Ytac0.
+ move => xy; move: (proj1 xy) => h1; Ytac0; Ytac w => //; ord_tac.
+ move => xy; move: (proj1 xy) => h1; Ytac0; Ytac w => //; ord_tac.
Qed.

Lemma omaxA x y z: ordinalp x -> ordinalp y -> ordinalp z ->
    omax x (omax y z) = omax (omax x y) z.
Proof.
move =>ox oy oz.
rewrite /omax.
case: (ord_le_to_el ox oy) => h1.
   Ytac0.
   case: (ord_le_to_el oy oz) => h2.
     by move: (ord_leT h1 h2) => h3; Ytac0; Ytac0.
   by case : (p_or_not_p (y <=o z)) => h3; [ord_tac | Ytac0; Ytac0].
case : (p_or_not_p (x <=o y)) => h2; [ord_tac | Ytac0].
case: (ord_le_to_el oy oz) => h3; first by Ytac0.
case : (p_or_not_p (y <=o z)) => h4; [ord_tac | Ytac0; Ytac0].
move: (ord_le_ltT (proj1 h3) h1) => h6.
by case : (p_or_not_p (x <=o z)) => h5; [ ord_tac| Ytac0].
Qed.

Lemma omax_p1 x y: ordinalp x -> ordinalp y ->
   x <=o (omax x y) /\ y <=o (omax x y).
Proof.
move => ox oy.
rewrite /omax.
move: (ord_leR oy) => yy.
move: (ord_leR ox) => xx.
case: (ord_le_to_ell ox oy).
+ by move => ->; Ytac0.
+ by move => xy; move: (proj1 xy) => h1; Ytac0.
+ move => xy; move: (proj1 xy) => h1.
  by case : (p_or_not_p (x <=o y)) => h2; [ord_tac | Ytac0].
Qed.

Lemma omax_p2 y (c:= cardinal (omax y omega0)):
    ordinalp y -> (infinite_c c /\ inc y (succ_c c)).
Proof.
move:OS_omega => oo oy; move:(omax_p1 oy oo) => [pa pb]; split.
   apply /infinite_c_P2.
   move: (ordinal_cardinal_le1 pb).
   by rewrite cardinal_Bnat.
apply /succ_cP; first by rewrite /c; fprops.
by split=> //; apply: ordinal_cardinal_le1.
Qed.

Lemma omax_p3 x y (c:= cardinal (omax (omax x y) omega0)):
   ordinalp x -> ordinalp y ->
   [/\ infinite_c c, inc x (succ_c c) & inc y (succ_c c)].
Proof.
move => ox oy.
move:(omax_p2 (OS_omax ox oy)); rewrite -/c; move => [ca cb].
move: (omax_p1 ox oy) => [ta tb].
have aux: forall t, t <=o omax x y -> inc t (succ_c c).
   move: (proj1 (CS_succ_c (proj1 ca))) => oc.
   have od: (omax x y) <o (succ_c c) by apply /(ord_ltP oc).
   move => t tc; apply /(ord_ltP oc); ord_tac.
by split => //; apply: aux.
Qed.

Lemma normal_ofs_restriction f C (E := succ_c C):
  infinite_c C-> normal_ofs f ->
  (forall x, inc x E -> inc (f x) E) ->
  normal_function (Lf f E E) E E.
Proof.
move => ice [nfa nfb] Esf.
have ff: function (Lf f E E) by apply: lf_function.
have fp: function_prop (Lf f E E) E E by split; aw.
have si: sincr_ofn (Lf f E E) E by move => a b aE bE /nfa; aw.
split => // a ae /nfb la; aw; rewrite la; congr union.
have asE: sub a E.
   have oe: ordinalp E by exact : (proj1 (CS_succ_c (proj1 ice))).
   move => t ta; exact:(ordinal_transitive oe ae ta).
have asf: sub a (source (Lf f E E)) by aw.
set_extens t.
  by move/funI_P => [z za ->]; apply /(Vf_image_P ff asf);ex_tac; aw;apply: asE.
move/(Vf_image_P ff asf) => [u ua]; move:(asE _ ua) => uE; aw => ->.
apply /funI_P; ex_tac.
Qed.

Definition card_max x y:= cardinal (omax (omax x y) omega0).

Lemma ofs_add_restr a y (c:= card_max a y) (E := succ_c c)
  (f:= Lf (fun z => a +o z) E E) :
  ordinalp a -> ordinalp y ->
  [/\ (forall x, inc x E -> inc (a +o x) E ),
      (forall x, x <=o y -> inc x E),
      normal_function f E E &
      forall x, inc x E -> Vf f x = a +o x].
Proof.
move => oa oy.
move: (omax_p3 oa oy); rewrite -/(card_max _ _) -/c -/E.
move => [icc ae ye].
have pa: forall x, x <=o y -> inc x E.
   move: (proj1 (CS_succ_c (proj1 icc))) => oc.
   have yc:y <o succ_c c by apply/(ord_ltP oc).
   move => x xe; apply/(ord_ltP oc); ord_tac.
have pb: forall x, inc x E -> inc (a +o x) E.
   by move => x xe;apply: succ_c_sum.
move: (normal_ofs_restriction icc (osum_normal oa) pb) => pc.
split => //x xe; rewrite /f; aw.
Qed.

Lemma ofs_mul_restr a y (c:= card_max a y) (E := succ_c c)
  (f:= Lf (fun z => a *o z) E E) :
  \0o <o a -> ordinalp y ->
  [/\ (forall x, inc x E -> inc (a *o x) E ),
      (forall x, x <=o y -> inc x E),
      normal_function f E E &
      forall x, inc x E -> Vf f x = a *o x].
Proof.
move => ap oy.
have oa: ordinalp a by ord_tac.
move: (omax_p3 oa oy); rewrite -/(card_max _ _) -/c -/E.
move => [icc ae ye].
have pa: forall x, x <=o y -> inc x E.
   move: (proj1 (CS_succ_c (proj1 icc))) => oc.
   have yc:y <o succ_c c by apply/(ord_ltP oc).
   move => x xe; apply/(ord_ltP oc); ord_tac.
have pb: forall x, inc x E -> inc (a *o x) E.
   by move => x xe;apply: succ_c_prod.
move: (normal_ofs_restriction icc (oprod_normal ap) pb) => pc.
split => //x xe; rewrite /f; aw.
Qed.

Definition iclosed_set E :=
   (ordinal_set E) /\
   (forall F, sub F E -> nonempty F -> (\osup F = \osup E \/ inc (\osup F) E)).
Definition iclosed_collection (E:property) :=
   ordinal_prop E /\
   (forall F, (forall x, inc x F -> E x) -> nonempty F -> E (\osup F)).

Lemma iclosed_ord x: ordinalp x -> iclosed_set x.
Proof.
move => ox.
have osx: ordinal_set x by move => t tb; ord_tac.
split => // F fo _.
move: (ord_ub_sup1 ox fo) => h.
case: (equal_or_not (union F) x) => e1; last by right; apply /(ord_ltP ox).
left; apply: (ord_sup_1cofinal _ osx); split => // a ax.
have au: a <o union F by rewrite e1; apply /ord_ltP.
have osf: ordinal_set F by move => t /fo tb; ord_tac.
move:(@ord_lt_sup F a osf au)=> [z za [zb _]]; ex_tac.
Qed.

Lemma iclosed_lim: iclosed_collection limit_ordinal.
Proof.
split; first by move => x [].
move => F fl nef.
have os: ordinal_set F by move => x /fl [].
case: (ord_sup_inVlimit os nef); [ by move /fl | done].
Qed.

Lemma iclosed_nonlim:
   ~(iclosed_collection (fun z => ordinalp z /\ ~limit_ordinal z)).
Proof.
move: omega_limit => pa; move: (pa) => [pb _ _].
move /(limit_ordinal_P1 pb): (pa) => [pc pd].
have pe: (forall x, inc x omega0 -> ordinalp x /\ ~ limit_ordinal x).
  move => x /(ord_ltP pb) xo; split; [| move => /omega_limit2 xx];ord_tac.
by move => [_ h]; move: (h _ pe pc); rewrite -pd; move=> [_].
Qed.

Lemma iclosed_fixpoints f (P:= fun z => ordinalp z /\ f z = z):
    normal_ofs f ->
    (iclosed_collection P) /\ non_coll P.
Proof.
move => nf; split; last first.
  apply:unbounded_non_coll; first by move => x [].
  move => x ox.
  move: (normal_ofs_fix nf ox) => [pa pb _].
  exists (the_least_fixedpoint_ge f x) => //; split => //; ord_tac.
split; [by move => t [] | move => F fp nef ].
move /normal_ofs_equiv1: (nf) => [pa pb].
have pc: (forall x, inc x F -> \0o <=o x) by move =>x /fp [ox _]; ord_tac1.
have pd: (fun_image F f) = F.
  set_extens t.
     by move => /funI_P [z zf ->]; rewrite (proj2 (fp _ zf)).
  by move => tf; apply /funI_P; ex_tac; rewrite (proj2 (fp _ tf)).
move: (pb _ pc nef); rewrite pd => h; split => //; apply: OS_sup.
move => t /pc ta;ord_tac.
Qed.

Lemma iclosed_fixpoints_fun f a :
  ordinalp a -> normal_function f a a -> iclosed_set (fixpoints f).
Proof.
move => oa na; move:(na) => [[ff sf tf] pb pc].
rewrite /fixpoints sf;split; first by move => t /Zo_S h; ord_tac0.
set E := Zo a _;move => F fp nef.
have subfa: sub F a by move => t /fp /Zo_S.
have ose: ordinal_set E by move =>t /Zo_S h; ord_tac.
move: (ord_sup_M fp ose) => l1.
have h: \osup E <=o a by apply:ord_ub_sup1 => //; apply : Zo_S.
case: (equal_or_not (\osup F) a) => sna.
  rewrite - sna in h; left; ord_tac.
case: (normal_function_equiv oa oa na subfa nef) => //.
have ->: (image_by_fun f F) = F.
  have aux: sub F (source f) by ue.
   set_extens t.
     by move => /(Vf_image_P ff aux) [z zf ->]; rewrite (Zo_hi (fp _ zf)).
   by move => zf; apply /(Vf_image_P ff aux); ex_tac; rewrite (Zo_hi (fp _ zf)).
move => fxp;right; apply: Zo_i => //.
apply /(ord_ltP oa); split => //;ord_tac.
Qed.

Lemma iclosed_normal_fun f x y:
  ordinalp x -> ordinalp y -> normal_function f x y ->
  iclosed_set (range (graph f)).
Proof.
move => ox oy nf; move: (nf)=> [[ff sf tf] pb pc]; split.
  move =>t /(f_range_graph ff); rewrite tf => h; ord_tac.
move => F fr [w wf].
set G := Zo (source f) (fun z => inc (Vf f z) F).
have sgx: sub G x by rewrite - sf; apply:Zo_S.
have neG: nonempty G.
  move /(range_fP ff): (fr _ wf) => [u uf uv]; exists u; apply:Zo_i => //; ue.
move: (ord_ub_sup1 ox sgx) => le1.
have sG: sub G (source f) by ue.
case: (equal_or_not (\osup G) x) => sx.
  have og: ordinal_set G by move => t /Zo_S; rewrite sf => ta; ord_tac.
  rewrite - sf in pb.
  left; apply: ord_sup_1cofinal; first split => //; last first.
    move =>t /(f_range_graph ff); rewrite tf => h; ord_tac.
  move => b /(range_fP ff) [u usf ->].
  have ua': u <o \osup G by rewrite sx; apply /(ord_ltP ox); ue.
  move: (ord_lt_sup og ua') => [v vg uv].
  move /Zo_hi: (vg) => vf;ex_tac; exact (proj1 (pb _ _ usf (Zo_S vg) uv)).
have <-: image_by_fun f G = F.
  set_extens t; first by move /(Vf_image_P ff sG)=> [u /Zo_hi h ->].
  move => tF; apply /(Vf_image_P ff sG).
  move /(range_fP ff): (fr _ tF)=> [u usf uv]; exists u => //.
  apply: Zo_i => //; ue.
case: (normal_function_equiv ox oy nf sgx neG) => // <-; right.
apply /(range_fP ff); exists (\osup G) => //; rewrite sf.
by apply/(ord_ltP ox).
Qed.

Lemma iclosed_normal_ofs1 f u (P := fun z => exists2 x, u <=o x & z = f x):
  ordinalp u -> normal_ofs1 f u ->
  (iclosed_collection P /\ non_coll P).
Proof.
move => ou [na nb].
move: (ofs_sincru na) => ha.
move: (osum_increasing5 ou na) =>[_ [b ub h]].
have oP: ordinal_prop P by move => x [y /ha uy ->].
split; last first.
  apply:unbounded_non_coll => // x ox.
  case: (ord_le_to_ee (proj32 ub) ox) => bx.
     exists (f x); [ by apply:h | exists x => //; ord_tac].
  exists (f b); [move: (h _ (ord_leR (proj32 ub))) => h1; ord_tac |by exists b].
split => //.
move => F Fi nef.
pose p z t:= u <=o t /\ z = f t.
pose g x := choose (p x).
set E:= fun_image F g.
have Cp: forall a z, u <=o a -> z = f a -> u <=o (g z) /\ z = f (g z).
  by move => a z ua zf; apply: (choose_pr (p:=p z)); exists a.
have CP1:forall a, u <=o a -> a = g (f a).
  move => a au; move: (Cp _ _ au (erefl (f a))) => [pa pb].
  case: (ord_le_to_ell (proj32 au) (proj32 pa)) => // hb.
      by move: (proj2 (na _ _ au hb)); rewrite {1} pb.
 by move: (proj2 (na _ _ pa hb)); rewrite - pb.
have Fv: F = fun_image E f.
  set_extens w.
    move => wF; apply/funI_P; move: (Fi _ wF) => [t ta tb]; exists t => //.
    by apply /funI_P; exists w => //; rewrite tb; apply: CP1.
  move /funI_P => [x xE ->].
  move /funI_P: (xE) => [z zf xf]; move:(Fi _ zf) => [t ta tb].
  by move: (CP1 _ ta); rewrite - tb - xf => <-; rewrite - tb.
have pa: forall x, inc x E -> u <=o x /\ g (f x) = x.
  move => w /funI_P [z zf zv]; move: (Fi _ zf) => [t ta tb].
  have tc: exists t, u <=o t /\ z = f t by exists t.
  move: (choose_pr tc); rewrite -/(g _) - zv; move => [la <-]; split => //.
have Ea: (forall x, inc x E -> u <=o x) by move => x /pa [].
move: nef => [t0 t0f].
have fit0: inc (g t0) E by apply/funI_P; ex_tac.
have ne: nonempty E by ex_tac.
have: u <=o \osup E.
  apply: (ord_leT (Ea _ fit0)); apply: ord_sup_ub => // t /Ea ut; ord_tac.
by move: (nb _ Ea ne); rewrite -Fv => ->; exists (union E).
Qed.

Lemma iclosed_normal_ofs f (P := fun z => exists2 x, ordinalp x & z = f x):
  normal_ofs f ->
  (iclosed_collection P /\ non_coll P).
Proof.
move => h.
have sb: normal_ofs1 f \0o by apply/normal_ofs_equiv1.
move: (iclosed_normal_ofs1 OS0 sb)=> [[pa pb] pc].
have aux: forall z, P z <-> exists2 x, \0o <=o x & z = f x.
  move => z; split; move => [x xp ->]; exists x => //; [ord_tac1 |ord_tac].
split => //; first split.
+ by move => t [x ox ->]; apply(ofs_sincr (proj1 h)).
+ by move => g pF nef; apply /aux; apply: pb => // x/pF => w; apply/aux.
+ by move => [E ev]; case:pc; exists E => x; split; [move/ev/aux| move/aux/ev].
Qed.

Lemma normal_fix_points_similara f C (E:= succ_c C): infinite_c C ->
  normal_ofs f ->
  (forall x, inc x E -> inc (f x) E) ->
  (ord_cofinal (Zo E (fun z => f z = z)) E).
Proof.
move => icE nf fee.
split => //; first by apply: Zo_S.
move => x xE.
move: (next_fix_point_small icE nf fee xE).
move/(succ_cP (proj1 icE)): (xE) => [ox ob].
move: (normal_ofs_fix nf ox) => []; set y := the_least_fixedpoint_ge f x.
by move => qa qb _ qc; exists y => //; apply:Zo_i.
Qed.

Lemma normal_fix_points_similarb f C (E:= succ_c C): infinite_c C ->
  normal_ofs f ->
  (forall x, inc x E -> inc (f x) E) ->
  cardinal (Zo E (fun z => f z = z)) = E.
Proof.
move => icE nf fee; apply:ord_cofinal_p4 => //.
exact: (normal_fix_points_similara icE nf fee).
Qed.

Indexing a collection of ordinals


Definition collectivising_srel (r: relation) :=
   forall x, r x x -> exists E, (forall y, inc y E <-> r y x).
Definition worder_rc r := worder_r r /\ collectivising_srel r.

Definition segmentr (r: relation) x :=
   choose (fun E => (forall y, inc y E <-> (r y x /\ y <> x))).

Definition ordinalr r x := ordinal (graph_on r (segmentr r x)).

Lemma segmentrP r x: collectivising_srel r -> r x x ->
   (forall y, (inc y (segmentr r x) <-> r y x /\ y <> x)).
Proof.
move => pa pb; move: (pa _ pb) => [E Ep].
pose p E:= forall y, inc y E <-> r y x /\ y <> x.
rewrite -/(p _); apply: (choose_pr); exists (E -s1 x) => y; split.
  by move/setC1_P => [/Ep sa sb].
by move => [/Ep sa sb]; apply /setC1_P.
Qed.

Lemma worder_rc_seg (r: relation) x:
  worder_rc r -> r x x ->
  worder_on (graph_on r (segmentr r x)) (segmentr r x).
Proof.
move => [pa pb] rxx; apply: (wordering_pr pa).
move => a /(segmentrP pb rxx) [pc _].
by move: (pa) => [[_ _ rr] _]; move: (rr _ _ pc) => [].
Qed.

Lemma OS_ordinalr r x:
  worder_rc r -> r x x -> ordinalp (ordinalr r x).
Proof. by move => pa /(worder_rc_seg pa) [pc _]; apply: OS_ordinal. Qed.

Lemma ordinalr_Mle r x y: worder_rc r -> r x y ->
   (ordinalr r x) <=o (ordinalr r y).
Proof.
move => pa px.
move: (proj33 (proj1 (proj1 pa)) _ _ px) => [rxx ryy].
move: (proj1 (worder_rc_seg pa rxx)) (proj1 (worder_rc_seg pa ryy)) => ha hb.
apply: order_cp2 => //.
have ss: sub (segmentr r x) (segmentr r y).
  move => t /(segmentrP (proj2 pa) rxx)[sa sb].
  move: (proj31 (proj1 (proj1 pa)) _ _ _ sa px) => ty.
  apply/(segmentrP (proj2 pa) ryy); split => //.
  move => ety; rewrite - ety in px.
  by move: (proj32_1 (proj1 pa) _ _ sa px).
apply/(order_cp0 (proj1 ha) (proj1 hb)) => u v.
by move /graph_on_P1 => [sa sb sc]; apply /graph_on_P1; split => //; apply: ss.
Qed.

Lemma ordinalr_Mlt r x y: worder_rc r -> r x y -> x <> y ->
   (ordinalr r x) <o (ordinalr r y).
Proof.
move => pa px xny.
move: (pa) => [[[rt ra rr] _] csr].
have [rxx ryy] := (rr _ _ px).
move: (segmentrP csr rxx) (segmentrP csr ryy) => sx sy.
have ss: sub (segmentr r x) (segmentr r y).
  move => t /sx [sa sb].
  move: (rt _ _ _ sa px) => ty; apply/sy; split => //.
  move => ety; rewrite - ety in px.
  by move: (ra _ _ sa px).
move: (proj1 (worder_rc_seg pa rxx)) (proj1 (worder_rc_seg pa ryy)) => ha hb.
have pb: sub (graph_on r (segmentr r x)) (graph_on r (segmentr r y)).
  apply/(order_cp0 (proj1 ha) (proj1 hb)) => u v.
  by move/graph_on_P1 => [sa sb sc]; apply /graph_on_P1; split => //; apply: ss.
have pc: forall z, r z z ->
   substrate (graph_on r (segmentr r z)) = (segmentr r z).
  move => z rzz; apply :graph_on_sr => a /(segmentrP csr rzz) [xx _].
  by move: (rr _ _ xx) => [].
have xsy: inc x (segmentr r y) by apply /sy.
apply: order_cp3 => //.
  split => // eq; move: (f_equal substrate eq).
  rewrite (pc _ rxx) (pc _ ryy) => h.
  by move: xsy; rewrite -h; move/sx =>[_].
rewrite (pc _ rxx); red; rewrite (pc _ ryy); split => //.
move => a b /sx [sa sb] /graph_on_P1 [h1 h2 h3]; apply /sx.
move: (rt _ _ _ h3 sa) => bx; split => // ebx.
by rewrite ebx in h3; move: (ra _ _ sa h3).
Qed.

Lemma ordinalr_segment r a x (b:=ordinalr r x):
   worder_rc r -> r x x -> a <=o b ->
   (exists2 y, r y y & a = ordinalr r y).
Proof.
move => woc rxx ab.
case: (equal_or_not a b); [by move ->; exists x | move => nab].
have iab: inc a b by apply /ord_ltP; [ord_tac | split ].
move: (worder_rc_seg woc rxx) => [pa pb].
move: (the_ordinal_iso1 pa) => [].
rewrite -/(ordinalr _ _) pb ordinal_o_sr.
set gr := (graph_on r (segmentr r x)); set f := (the_ordinal_iso gr).
set oor := (ordinal_o (ordinalr r x)).
move => sa sb [[[ff injf] [_ sjf]] sf tf] sd.
have asf: inc a (source f) by ue.
move: (Vf_target ff asf); rewrite tf.
set y := Vf f a; set rs:= (graph_on r (segmentr r y)).
move /(segmentrP (proj2 woc) rxx) => [ryx xny].
move: (proj1 (proj1 woc)) => or.
move: (proj33 or _ _ ryx) => [ryy _].
move:(segmentrP (proj2 woc) ryy) => sP.
set g:= Lf (Vf f) a (segmentr r y).
move: (ab) => [oa ob sab].
suff oih:order_isomorphism g (ordinal_o a) rs.
  have h : ordinal_o a \Is rs by exists g.
  move: (worder_invariance h (ordinal_o_wor oa)) => tt.
  by exists y => //; rewrite - (ordinal_o_isu2 tt oa (orderIS h)).
have srr: substrate rs = (segmentr r y).
   by apply:graph_on_sr => u /sP [/(proj33 or) []].
have saf: sub a (source f) by ue.
have la: lf_axiom (Vf f) a (segmentr r y).
  move => s ssa; apply /sP; rewrite /y; move: (saf _ ssa) => isf.
  split; last first.
    move/(injf _ _ isf asf) => esa.
    by case: (ordinal_irreflexive oa); rewrite -{1} esa.
  have:gle oor s a.
    apply /graph_on_P1;rewrite - sf; split => //.
    exact: (ordinal_transitive oa ssa).
  by move /(sd s a isf asf) => /graph_on_P1 [].
hnf; rewrite (ordinal_o_sr) srr.
split => //; [ fprops | apply: (order_from_rel _ or) | |].
  red; rewrite /g; aw; split => //; apply: lf_bijective => //.
    by move => u v ua va; apply injf; apply: saf.
  move => c /sP [c1 c2].
  have: inc c (target f).
     rewrite tf; apply /(segmentrP (proj2 woc) rxx).
     move: (proj31 or _ _ _ c1 ryx)=> cx; split => //.
     by move => ecx; rewrite -ecx in ryx;move: (proj32 or _ _ c1 ryx).
  move /sjf => [d dsf dv]; exists d; last by rewrite dv.
  have od: ordinalp d by rewrite sf in dsf; exact: (ordinal_hi ob dsf).
  case:(ord_le_to_si oa od) => // sad.
  have: gle oor a d by apply /sub_gleP; split => //; ue.
  move /(sd _ _ asf dsf) => /graph_on_P1 [_ _];rewrite dv -/y => yc.
  case: c2; exact (proj32 or _ _ c1 yc).
hnf; rewrite /g;aw => u v ua va; aw.
move: (saf _ ua) (saf _ va) =>ub vb.
move: (sd u v ub vb) => h;split.
  move/sub_gleP=> [_ _ h1]; apply/graph_on_P1; split; try apply: la => //.
  suff:gle gr (Vf f u) (Vf f v) by move/graph_on_P1 =>[].
  by apply /h; apply/sub_gleP; rewrite - sf.
move/graph_on_P1 => [_ _ h1]; apply/sub_gleP; split => //.
suff:gle gr (Vf f u) (Vf f v) by move/h /sub_gleP=> [].
apply/graph_on_P1; split => //; Wtac.
Qed.

Definition ordinals r a := choose (fun x => r x x /\ a = ordinalr r x).

Lemma ordinalsP r a: worder_rc r ->
  (exists2 x, r x x & a = ordinalr r x) ->
  (r (ordinals r a) (ordinals r a) /\ ordinalr r (ordinals r a) = a).
Proof.
move => pa [x rxx av].
pose p x := (r x x /\ a = ordinalr r x).
rewrite /ordinals -/p.
have exp : exists x, p x by exists x.
by move: (choose_pr exp); set q := (choose p); move => [rqq ->].
Qed.

Lemma ordinalsrP r x: worder_rc r -> r x x ->
  ordinals r (ordinalr r x) = x.
Proof.
move => pa pb.
set a := (ordinalr r x).
have: (exists2 x, r x x & a = ordinalr r x) by exists x.
move /(ordinalsP pa); set q := ordinals r a; move => [rqq qv].
ex_middle bad.
case: (worderr_total (proj1 pa) rqq pb) => h.
  by move: (ordinalr_Mlt pa h bad) => [_]; rewrite qv.
by move: (ordinalr_Mlt pa h (nesym bad))=> [_]; rewrite qv.
Qed.

Lemma ordinals_non_coll1 r: worder_rc r ->
   (non_coll (fun x => r x x)) ->
   (forall a, ordinalp a -> exists2 x, r x x & a = ordinalr r x).
Proof.
move => pa pb a oa; ex_middle npa; case: pb.
exists (Zo (fun_image a (ordinals r)) (fun z => r z z)).
move => x; split; first by move /Zo_hi.
move => rxx; apply /Zo_P; split => //; apply /funI_P.
have osx:= (OS_ordinalr pa rxx).
have eq:= (ordinalsrP pa rxx).
have: ordinalr r x <o a.
  by case: (ord_le_to_el oa osx) => // alx; move:(ordinalr_segment pa rxx alx).
move /ord_ltP0 => [_ _ za]; ex_tac.
Qed.

Lemma ordinals_Mle r a b: worder_rc r ->
   (non_coll (fun x => r x x)) ->
   a <=o b -> r (ordinals r a) (ordinals r b).
Proof.
move => ha hb ab.
move: (ordinalsP ha (ordinals_non_coll1 ha hb (proj31 ab))).
move: (ordinalsP ha (ordinals_non_coll1 ha hb (proj32 ab))).
set sa := (ordinals r a); set sb := (ordinals r b).
move => [rbb vb][raa va].
case: (worderr_total (proj1 ha) raa rbb) => // h.
move:(ordinalr_Mle ha h); rewrite va vb => ba.
by move: raa; rewrite /sa /sb (ord_leA ab ba).
Qed.

Lemma ordinals_Mlt r a b: worder_rc r ->
   (non_coll (fun x => r x x)) ->
   a <o b ->
  (r (ordinals r a) (ordinals r b) /\ (ordinals r a) <> (ordinals r b)).
Proof.
move => pa pb [pc pd]; move:(ordinals_Mle pa pb pc) => h.
split => // sv.
move: (pc) => [oa ob _].
have sa: (exists2 x, r x x & a = ordinalr r x) by apply:ordinals_non_coll1.
have sb: (exists2 x, r x x & b = ordinalr r x) by apply:ordinals_non_coll1.
by case: pd; rewrite - (proj2 (ordinalsP pa sa)) - (proj2 (ordinalsP pa sb)) sv.
Qed.

Lemma collectivising_srel_ord (r:relation):
  (forall x y, r x y -> x <=o y) -> collectivising_srel r.
Proof.
move => h1 x /h1 xx.
have ox: ordinalp x by ord_tac.
exists (Zo (succ_o x) (r^~ x)); move => y;split; first by move /Zo_hi.
by move => ryx; apply:Zo_i => //; apply /(ord_leP ox); apply: h1.
Qed.

Lemma collectivising_srel_ord_seg (r:relation) :
  (forall x y, r x y -> x <=o y) ->
  forall x, r x x -> segmentr r x = Zo x (fun z => r z x).
Proof.
move => ro x rxx.
move: (proj31 (ro _ _ rxx)) => ox.
move:(collectivising_srel_ord ro) => cr.
set_extens t.
  move /(segmentrP cr rxx) => [pa pb]; apply:Zo_i => //.
  by apply/(ord_ltP ox); split => //; apply: ro.
move => /Zo_P[pa pb]; apply /(segmentrP cr rxx); split => //.
by move=> tx; case: (ordinal_decent ox pa); rewrite {2} tx.
Qed.

Lemma worder_rc_ord (r:relation) :
  (forall x y, r x y -> x <=o y) ->
  (order_r r) -> (forall x y, r x x -> r y y -> (r x y \/ r y x)) ->
  worder_rc r.
Proof.
move=> h1 h2 h3; split.
  split => // x hx [w wx].
  move: (proj31 (h1 _ _ (hx _ wx))) => ow.
  move: (least_ordinal4 ow wx (p:=inc^~x)); set y := least_ordinal _ _.
  move=> [oy yx yl]; rewrite /least (graph_on_sr hx); exists y; split => //.
  move => t tx; apply /graph_on_P1; split => //.
  case: (h3 _ _ (hx _ yx) (hx _ tx)) => // /h1 ty.
  move: (proj31 (h1 _ _ (hx _ tx))) => ot.
  by move: (yl _ ot tx) => yt; rewrite (ord_leA ty yt); apply: hx.
move => x /h1 xx.
have ox: ordinalp x by ord_tac.
exists (Zo (succ_o x) (r^~ x)); move => y;split; first by move /Zo_hi.
by move => ryx; apply:Zo_i => //; apply /(ord_leP ox); apply: h1.
Qed.

Lemma worder_rc_op (p:property) :
  worder_rc (fun x y => [/\ p x, p y & x <=o y]).
Proof.
apply: worder_rc_ord.
- by move => x y [_ _].
- split.
  + move =>x y z [pa pb pc][pd pe pf]; split => //; ord_tac.
  + move =>x y [_ _ pc][ _ _ pf] ; ord_tac.
  + move => x y [pa pb pc]; split; split => //; apply: ord_leR; ord_tac.
- move => x y [pa _ [pc _ _]][ pb _ [pf _ _]].
  by case: (ord_le_to_ee pc pf) => le; [left | right]; split.
Qed.

Lemma ordinalrsP (p: property) (r := fun x y => [/\ p x, p y & x <=o y])
   x (y := ordinalr r x):
   ordinal_prop p -> p x -> (ordinalp y) /\ ordinals r y = x.
Proof.
move => op px; move:(op _ px) => ox.
move: (worder_rc_op p) => pa.
have rxx: r x x by split => //; ord_tac.
move:(ordinalsrP pa rxx) => h; split => //; by apply: OS_ordinalr.
Qed.

Lemma ordinals_set_iso E (p := inc ^~E)
      (r:= fun x y => [/\ p x, p y & x <=o y])
      (A:= ordinal (graph_on ordinal_le E)):
  (ordinal_set E) ->
  (lf_axiom (ordinals r) A E) /\
  order_isomorphism (Lf (ordinals r) A E) (ordinal_o A) (graph_on ordinal_le E).
Proof.
move => pa.
move: (worder_rc_op p) ; rewrite -/r => pc.
have ose: (forall a, inc a E -> a <=o a) by move => a /pa op; ord_tac.
have rxx: forall x, r x x <-> inc x E.
  move => x; split; [ by move =>[] | move => xE;split; fprops ].
pose H a := (exists2 x, r x x & a = ordinalr r x).
have pd: forall a, H a ->
    (r (ordinals r a) (ordinals r a) /\ ordinalr r (ordinals r a) = a).
  by move => a ha; apply: ordinalsP.
have pe: forall x, inc x E -> ordinals r (ordinalr r x) = x.
  by move => x /rxx xe;apply: ordinalsrP.
move: (wordering_ordinal_le_pr pa) => [sc sd].
have ra: forall x, inc x E -> inc (ordinalr r x) A.
  move => x /rxx xE; suff: (ordinalr r x) <o A by move /ord_ltP0 => [].
  move: (worder_rc_seg pc xE) => []; rewrite -/r => [sa sb].
  move: (segmentrP (proj2 pc) xE); rewrite -/r => sp.
  apply: order_cp3 => //; first split.
       apply /(order_cp0 (proj1 sa) (proj1 sc))=> u v.
       by move/graph_on_P1 => [_ _ uv];apply/graph_on_P1.
    move => h;move:(f_equal substrate h); rewrite sb sd => ss.
    by move /rxx:(xE); rewrite - ss; move /sp => [_].
  red; rewrite sb sd;split; first by move =>t /sp [] [].
  move => u v /sp [[pu px ux] nux] /graph_on_P1 [ve _ vu]; apply /sp; split.
    split => //; ord_tac.
    move => vx; case: nux; rewrite vx in vu; ord_tac.
have rb:forall a, inc a A -> H a.
  move => a aA.
  move: (the_ordinal_iso1 sc); rewrite -/A; set f:= the_ordinal_iso _.
  rewrite /order_isomorphism sd ordinal_o_sr.
  move => [sa sb [[[ff injf] [_ sjf]] sf tf] isf].
  red in isf; rewrite sf in injf isf.
  set x := Vf f a.
  have xe: r x x by apply /rxx;rewrite - tf /x; Wtac.
  move:(worder_rc_seg pc xe)=> [ta tb].
  have oA:ordinalp A by exact: (OS_ordinal sc).
  have oa :=(ordinal_hi oA aA).
  have saA :=(ordinal_transitive oA aA).
  exists x => //; symmetry;rewrite /ordinalr;apply: ordinal_o_isu2 => //.
  have la: lf_axiom (Vf f) a (segmentr r x).
    move => t ita; move: (saA _ ita) => tA.
    have [[_ _ lte] nta]: t <o a by apply /ord_ltP.
    have: gle (ordinal_o A) t a by apply /sub_gleP.
    move /(isf _ _ tA aA) => /graph_on_P1.
    move => h; apply /(segmentrP (proj2 pc) xe); split => //.
    by move => hh; case: nta; apply/ (injf _ _ tA aA).
  apply:orderIS;exists (Lf (Vf f) a (segmentr r x)).
  red; rewrite tb ordinal_o_sr; split; fprops.
    red; aw; split => //; apply: lf_bijective => //.
      move => u v /saA ua /saA va; apply /(injf _ _ ua va).
    move => y /(segmentrP (proj2 pc) xe) [[yE _ rxy] xny].
    rewrite tf in sjf;move: (sjf _ yE) => [];rewrite sf => [b bA bb].
    case: (ord_le_to_el (ordinal_hi oA aA) (ordinal_hi oA bA)).
      move => [_ _ ab]; have: gle (ordinal_o A) a b by apply /sub_gleP.
      move /(isf _ _ aA bA) => /graph_on_P1 [_ _];rewrite bb - /x => xy.
      case: xny; ord_tac.
    move /(ord_ltP oa) => ba; ex_tac.
  red; aw;move => u v ua va; aw; move: (saA _ ua) (saA _ va) => uA vA; split.
    move/sub_gleP=> [_ _ h1]; have: gle (ordinal_o A) u v by apply /sub_gleP.
    move /(isf _ _ uA vA) => /graph_on_P1 [_ _ le].
    apply/graph_on_P1; split;try apply: la => //; split => //; red; Wtac.
  move /graph_on_P1 => [_ _ [_ _ le]].
  have:(gle (graph_on ordinal_le E) (Vf f u) (Vf f v)).
    apply/graph_on_P1; split => //; Wtac.
  by move /(isf _ _ uA vA) => /sub_gleP [_ _ h1]; apply/sub_gleP.
set fp := ordinals r.
have rc: forall a, inc a A -> inc (fp a) E /\ ordinalr r (fp a) = a.
  by move => a /rb /pd [/rxx sa sb].
set f := Lf fp A E.
have lfa: lf_axiom fp A E by move => t /rc [].
have bf: bijection_prop f A E.
  red;rewrite /f; aw; split => //; apply: lf_bijective.
  - by exact.
  - by move => u v /rc [_ e1] /rc [_ e2] e3; rewrite - e1 - e2 e3.
  - by move => y yE; rewrite -(pe _ yE); move: (ra _ yE) => h; ex_tac.
split => //.
split => //; [ apply: ordinal_o_or | fprops | by rewrite ordinal_o_sr sd |].
hnf; rewrite /f; aw; move => x y xA yA; aw.
move: (rc _ xA) (rc _ yA) => [ta tb][tc td]; split => h.
  move:(proj2 (worder_total sc));rewrite sd => to;case: (to _ _ ta tc) => //hh.
  move /sub_gleP:h => [_ _ ce].
  suff: x = y by move => h1; move: hh; rewrite h1.
  move/graph_on_P1:hh => /(ordinalr_Mle pc).
  by rewrite tb td; move => [_ _ cp]; apply: extensionality.
move/graph_on_P1:h => /(ordinalr_Mle pc).
by rewrite tb td; move => [_ _ cp];apply/sub_gleP.
Qed.

Lemma ordinals_set_normal E (p := inc ^~E)
      (r:= fun x y => [/\ p x, p y & x <=o y])
      (A:= ordinal (graph_on ordinal_le E))
      (f:= Lf (ordinals r) A E):
  (iclosed_set E) ->
  normal_function f A E.
Proof.
move => [pa pb].
move: (proj2 (ordinals_set_iso pa)); rewrite -/f -/A.
move => [o1 o2 [[[ff injf] [_ sjf]] sf tf] fincr].
move: (worder_rc_op p) ; rewrite -/r => pc.
move: (wordering_ordinal_le_pr pa) => [sc sd].
rewrite sd in tf; rewrite ordinal_o_sr in sf.
have fi: forall a b, inc a A -> inc b A -> a <o b -> Vf f a <o Vf f b.
   red in fincr; rewrite sf in fincr injf.
   move => a b asf bsf [lab nab]; split.
     have: gle (ordinal_o A) a b by move: lab => [_ _ lab]; apply/sub_gleP.
     by move /(fincr _ _ asf bsf) => /graph_on_P1 [].
   by move => h; move: (injf _ _ asf bsf h).
split => // a aA la.
have oA:ordinalp A by exact: (OS_ordinal sc).
set F :=(image_by_fun f a).
have asf: sub a (source f) by rewrite sf; apply: (ordinal_transitive oA).
have sfe: sub F E by rewrite -tf ; apply:fun_image_Starget1.
have nef: nonempty F.
   exists (Vf f \0o); apply /(Vf_image_P ff asf); exists \0o => //.
   exact (proj32 la).
have osF: ordinal_set F by move => t /sfe; apply: pa.
have faE: inc (Vf f a) E by rewrite -tf; Wtac.
move: (ord_sup_ub pa faE) => suE.
have ofa: ordinalp (Vf f a) by apply: pa.
have bfa : ordinal_ub F (Vf f a).
  move => t /(Vf_image_P ff asf) [u ua ->].
  move: (ordinal_transitive oA aA ua) => uA.
  move /(ord_ltP (proj31 la)): ua => ua.
  exact (proj1 (fi _ _ uA aA ua)).
move: (ord_ub_sup osF ofa bfa) => pr1.
case: (pb _ sfe nef) => H; first by rewrite - H in suE; ord_tac.
rewrite tf in sjf; move: (sjf _ H); rewrite sf; move => [b bA bv].
move: (ordinal_hi oA aA) (ordinal_hi oA bA) => oa ob.
case: (ord_le_to_ell oa ob) => ab; first by ue.
  move: (fi _ _ aA bA ab); rewrite bv => h; ord_tac.
have sb: succ_o b <o a by move /(limit_ordinal_P3): la => [ _]; apply.
have sbb: inc (succ_o b) a by apply /(ord_ltP oa).
have osbA: inc (succ_o b) A by apply: (ordinal_transitive oA aA).
move:(fi _ _ bA osbA (ord_succ_lt ob)) => h.
have: inc (Vf f (succ_o b)) F by apply/(Vf_image_P ff asf); ex_tac.
move /(ord_sup_ub osF); rewrite - bv => h1; ord_tac.
Qed.

Definition ordinalsE E B :=
  Lf (ordinals (fun x y => [/\ inc x B, inc y B & x <=o y])) E E.

Lemma ordinals_set_normal1 C (E:= succ_c C) B (f:= ordinalsE E B):
  infinite_c C -> iclosed_set B -> ord_cofinal B E ->
  [/\ lf_axiom (ordinals (fun x y=> [/\ inc x B, inc y B & x <=o y])) E E ,
    normal_function f E E & image_of_fun f = B].
Proof.
move => pa pb pc.
move: (ord_cofinal_p5 pa pc); rewrite -/E => eq1.
move: (ordinals_set_normal pb) (ordinals_set_iso (proj1 pb)).
rewrite eq1; set g := (Lf (ordinals _) E B); move => [p1 p2 p3] [pd pe].
have sbe: sub B E by move: pc => [].
have pf: lf_axiom (ordinals (fun x y=> [/\ inc x B, inc y B & x <=o y])) E E
   by move => t /pd /sbe.
have ff: function f by apply: lf_function.
have fg: function g by apply: lf_function.
have oe: ordinalp E by move:(succ_c_pr1 (proj1 pa)) => [[]].
have sf: forall x, inc x E -> Vf f x = Vf g x.
   move => x xe; rewrite /f/ordinalsE/g; aw.
have imf: image_of_fun f = B.
   set_extens t.
     move /(Vf_image_P1 ff);rewrite /f/ordinalsE; aw.
     by move=> [u ua ->]; aw; apply: pd.
   move => tb;move: pe => [_ _ [[_ [_ p4]] _ _] _].
   have ttg: inc t (target g) by rewrite /g; aw.
   have sg: source g = E by rewrite /g; aw.
   move: (p4 _ ttg) => [u ua <-]; rewrite - sf; last by ue.
   apply /(Vf_image_P1 ff); exists u => //;rewrite /f/ordinalsE; aw; ue.
split => //; split => //.
+ by rewrite /f /ordinalsE/function_prop;aw.
+ by move => x y xe ye xy; rewrite sf // sf //; apply: p2.
+ move => x xe lx; rewrite sf // (p3 x xe lx).
  have sxe :=(ordinal_transitive oe xe).
  have sa: sub x (source g) by rewrite /g; aw.
  have sb: sub x (source f) by rewrite /f/ordinalsE; aw.
  congr union; set_extens y.
    move /(Vf_image_P fg sa) => [u ux ->]; rewrite - sf //; last by apply: sxe.
    apply /(Vf_image_P ff sb); ex_tac.
  move /(Vf_image_P ff sb) => [u ux ->]; rewrite sf //; last by apply: sxe.
  apply /(Vf_image_P fg sa); ex_tac.
Qed.

Lemma ordinals2_extc C1 C2 B1 B2
    (E1 :=succ_c C1)(E2 :=succ_c C2) :
  infinite_c C1 -> infinite_c C2 ->
  iclosed_set B1 -> iclosed_set B2 ->
  ord_cofinal B1 E1 -> ord_cofinal B2 E2 ->
  C1 <=c C2 -> B1 = B2 \cap E1 ->
  agrees_on E1 (ordinalsE E1 B1) (ordinalsE E2 B2).
Proof.
move => ic1 ic2 cl1 cl2 cf1 cf2 c1c2 iv.
move: (ordinals_set_normal1 ic1 cl1 cf1).
move: (ordinals_set_normal1 ic2 cl2 cf2).
rewrite -/E1 -/E2.
have E1E2: sub E1 E2.
   move => x /(succ_cP (proj1 ic1)) [sa sb]; apply/(succ_cP (proj1 ic2)).
   split => //; co_tac.
set f1 := (ordinalsE E1 B1); set f2 := (ordinalsE E2 B2).
move => [ax2 [[ff2 sf2 tf2] ninc2 cont2] im2].
move => [ax1 [[ff1 sf1 tf1] ninc1 cont1] im1].
have B1B2: sub B1 B2 by rewrite iv; apply: subsetI2l.
have oe2: ordinalp E2 by move:(succ_c_pr1 (proj1 ic2)) => [[]].
have oe1: ordinalp E1 by move:(succ_c_pr1 (proj1 ic1)) => [[]].
have ob2: ordinal_set B2 by move => t /(proj1 cf2) te; ord_tac.
have ob1: ordinal_set B1 by move => t /B1B2 /ob2.
move: (ordinals_set_iso ob1) => [].
rewrite (ord_cofinal_p5 ic1 cf1).
move: (ordinals_set_iso ob2) => [].
rewrite (ord_cofinal_p5 ic2 cf2).
set r1 := (fun x0 y : Set => [/\ inc x0 B1, inc y B1 & x0 <=o y]).
set r2 := (fun x0 y : Set => [/\ inc x0 B2, inc y B2 & x0 <=o y]).
rewrite -/E1 -/E2; move => ua2 ub2 ua1 ub1.
move: (inverse_order_is ub2).
set g2 := (Lf (ordinals r2) E2 B2); set g2' := inverse_fun g2 => ub3.
have sr2: (substrate (graph_on ordinal_le B2)) = B2.
  rewrite graph_on_sr // => a /ob2 or; ord_tac.
have sr1: (substrate (graph_on ordinal_le B1)) = B1.
  rewrite graph_on_sr // => a /ob1 or; ord_tac.
move: (ub3) => [_ _ bf _]; move: bf; rewrite sr2 ordinal_o_sr => bg2.
have sg2: source g2' = B2 by move: bg2 => [_ fb _].
have sa: forall x, inc x E1 -> inc (Vf g2' (ordinals r1 x)) E2.
  move => x /ua1 /B1B2 xsf; move: bg2 => [[[fa _] _] _ fc]; Wtac.
set g3 := Lf (fun x => (Vf g2' (ordinals r1 x))) E1 E2.
have isoc: order_morphism g3 (ordinal_o E1) (ordinal_o E2).
  move: ub1 ub3 => [pa _ _ pb] [_ pc _ pd].
  split => //.
    by rewrite !ordinal_o_sr /g3; hnf; aw; split => //; apply:lf_function.
  hnf;rewrite /g3; aw => x y xe1 ye1; aw.
  move: (pb x y); aw => h; move: (h xe1 ye1).
  set x' := (ordinals r1 x); set y' := (ordinals r1 y) => hh.
  have x'b2: inc x' B2 by apply/B1B2 /ua1.
  have y'b2: inc y' B2 by apply/B1B2 /ua1.
  move:(pd x' y'); rewrite sg2 => h'; move: (h' x'b2 y'b2) => hh'.
  apply: (iff_trans hh); split => //.
    by move => /graph_on_P1 [_ _ ok]; apply /hh' /graph_on_P1.
  move/hh' /graph_on_P1 => [_ _ ok]; apply /graph_on_P1.
  by split => //; apply:ua1.
set g4 := Lf id E1 E2.
have isod : order_morphism g4 (ordinal_o E1) (ordinal_o E2).
  move: isoc => [pa pb _ _]; split => //.
    rewrite !ordinal_o_sr /g4; hnf; aw; split => //; apply: lf_function => //.
  red;rewrite /g4; aw => x y xe1 ye1; aw; split => //.
      move => /graph_on_P1 [_ _ ok]; apply /graph_on_P1.
     by split => //; apply:E1E2.
  by move /graph_on_P1 => [_ _ ok]; apply /graph_on_P1.
have wo2: worder (ordinal_o E2) by apply: ordinal_o_wor.
have wo1: worder (ordinal_o E1) by apply: ordinal_o_wor.
have s1: segmentp (ordinal_o E2) (range (graph g3)).
   move: isoc=> [ _ _ [fg3 sg3 tg3] _].
   split.
     move => t /(range_fP fg3); rewrite - tg3; move => [ x xe1 ->]; Wtac.
   move => x y /(range_fP fg3) [x1 x1s ->] l2.
   have xe1: inc x1 E1 by move:x1s; rewrite sg3 ordinal_o_sr.
   move /ordo_leP: (l2) => [ye2 gx ll]; apply /(range_fP fg3).
   have ve2: inc (Vf g3 x1) E2 by rewrite /g3; aw; apply: sa.
   move: ub2 => [_ _ [bg sg tg2] h2]; move: (h2 y (Vf g3 x1)); aw => h3.
   move /(h3 ye2 ve2): l2.
   have inc1: inc (ordinals r1 x1) B1 by apply :ua1.
   have eq2: (ordinals r2 (Vf g3 x1)) = ordinals r1 x1.
     transitivity (Vf g2 (Vf g3 x1)); first by rewrite /g2; aw.
     by rewrite /g3; aw; apply: inverse_V => //; rewrite tg2 sr2; apply /B1B2.
   rewrite eq2; move /graph_on_P1=> [sa1 sb1 sc1].
   move: ub1=> [_ _ [bg1 sg1 tg1] _].
   have y'b1: inc (ordinals r2 y) (target (Lf (ordinals r1) E1 B1)).
       rewrite tg1 sr1 iv; apply /setI2_P; split => //.
       move: (proj1 cf1 _ inc1) => /(ord_ltP oe1) lt2.
       apply /(ord_ltP oe1); ord_tac.
   have eq3: (ordinals r2 y) = Vf g2 y by rewrite /g2; aw.
   move: (proj2 (proj2 bg1) _ y'b1) => [z]; rewrite lf_source => ze1; aw.
   move => zv; rewrite /g3; aw; exists z => //; aw.
   by rewrite zv eq3 inverse_V2 // sg ordinal_o_sr.
have s2:segmentp (ordinal_o E2) (range (graph g4)).
  have rg4: (range (graph g4)) = E1.
    move: isod=> [ _ _ [fg4 _ _] _].
    set_extens t.
      move/(range_fP fg4); rewrite /g4; aw; move => [x xsf ->]; aw.
    move => xe1; apply/(range_fP fg4); rewrite /g4; aw; ex_tac; aw.
  red; rewrite rg4 ordinal_o_sr; split => // x y xe1 /graph_on_P1 [ye2 _ xy].
  have yx: y <=o x by split => //; ord_tac.
  have xE: x <o E1 by apply /ord_ltP => //.
  apply /ord_ltP => //; ord_tac.
move: (isomorphism_worder_unique wo1 wo2 s1 s2 isoc isod) => g3g4.
hnf; rewrite sf1 sf2; split => // x xe1.
have xe2 := (E1E2 _ xe1).
rewrite /f1 /f2 /ordinalsE; aw; rewrite -/r1 -/r2.
move: ub2 => [_ _ [bg _ tg] _].
have tg2: target g2 = B2 by move: tg; rewrite -/g2 sr2.
move: (f_equal (Vf^~ x) g3g4); rewrite /g3 /g4; aw => eq1.
move: (f_equal (Vf g2) eq1); rewrite {2} /g2; aw.
by rewrite inverse_V //; rewrite tg2; apply /B1B2/ ua1.
Qed.

Definition ordinalsf (p: property) :=
   ordinals (fun x y => [/\ p x, p y & x <=o y]).

Lemma ordinals_col_p1 (p: property) (f := ordinalsf p):
   (forall x, p x -> ordinalp x) -> (non_coll p) ->
 [/\
    forall a, ordinalp a -> p (f a),
    forall x, p x -> exists2 a, ordinalp a & x = f a,
    forall a b, a<=o b -> (f a) <=o (f b),
    forall a b, a<o b -> (f a) <o (f b) &
    (forall a b, ordinalp a -> ordinalp b -> (a<=o b <-> (f a) <=o (f b))) /\
    forall a b, ordinalp a -> ordinalp b -> (a<o b <-> (f a) <o (f b))].
Proof.
move => pa pb.
set r := (fun x y => [/\ p x, p y & x <=o y]).
have pc: (non_coll (fun x => r x x)).
  move => [E Ep]; case: pb; exists E => x; split; first by move /Ep => [].
  move => px; apply /Ep; split => //; move: (pa _ px) => ox; ord_tac.
move: (worder_rc_op p) => pd.
have le0: forall a, ordinalp a -> p (f a).
  by move => a oa; move: (ordinals_Mle pd pc (ord_leR oa)) => [].
have lt1: forall a b, a <o b -> (f a) <o (f b).
  by move => a b h; move: (ordinals_Mlt pd pc h) => [[_ _]].
have le1: forall a b, a<=o b -> (f a) <=o (f b).
  by move => a b h; move: (ordinals_Mle pd pc h) => [_ _].
have lt2:forall x, p x -> exists2 a, ordinalp a & x = f a.
  move => x px.
  have h: r x x by split => //; move: (pa _ px) => ox; ord_tac.
  move:(ordinalsrP pd h) (OS_ordinalr pd h); rewrite -/r -/f => h1 h2.
  by exists (ordinalr r x).
split => //; split.
  move => a b oa ob; split => h; first by apply: le1.
  case: (ord_le_to_el oa ob) => // /lt1 cp; ord_tac.
move => a b oa ob; split => h; first by apply: lt1.
case: (ord_le_to_el ob oa) => // /le1 cp; ord_tac.
Qed.

Lemma ordinals_col_p2 (p: property) (f := ordinalsf p):
   (iclosed_collection p) -> non_coll p ->
   normal_ofs f.
Proof.
move => [pa pb] pc.
set r := (fun x y => [/\ p x, p y & x <=o y]).
move: (ordinals_col_p1 pa pc); rewrite -/r -/f; move => [s1 s2 s3 s4 [s5 s6]].
split=> // a la.
move: (la) => [oa zea lla].
set F :=(fun_image a f).
have nef: nonempty F by exists (f \0o); apply /funI_P; exists \0o.
have Fp: (forall x, inc x F -> p x).
  move => t /funI_P[z za ->]; apply:s1; ord_tac.
have osF: ordinal_set F by move => t /Fp/pa.
move: (pb _ Fp nef) => puF.
have bfa : ordinal_ub F (f a).
  by move => t /funI_P [z /(ord_ltP oa) [za _] ->]; apply:s3.
have ofa: ordinalp (f a) by apply: (pa _ (s1 _ oa)).
move: (ord_ub_sup osF ofa bfa).
move: (s2 _ puF) => [b ob bv]; rewrite bv => p1.
case: (ord_le_to_ell oa ob) => cab; first by rewrite cab.
  move:(s4 _ _ cab) => [lfab _]; ord_tac.
move:(s4 _ _ (ord_succ_lt ob)) => h.
have sb: succ_o b <o a by move /(limit_ordinal_P3): la => [ _]; apply.
have sbb: inc (succ_o b) a by apply /(ord_ltP oa).
have: inc (f (succ_o b)) F by apply/funI_P; ex_tac.
move /(ord_sup_ub osF); rewrite bv=> h1; ord_tac.
Qed.

Definition first_derivation (f: fterm) :=
  (ordinalsf (fun z => ordinalp z /\ f z = z)).

Lemma iclosed_col_f0 (p: property) (f := ordinalsf p) (x:= f \0o):
  (iclosed_collection p) -> (non_coll p) ->
  (p x /\ (forall z, p z -> x <=o z)).
Proof.
move => [pa pb] pc.
set r := (fun x y => [/\ p x, p y & x <=o y]).
move: (ordinals_col_p1 pa pc); rewrite -/r -/f; move => [s1 s2 s3 s4 [s5 s6]].
split; first by apply: s1; fprops.
move => z /s2 [a oa ->]; apply: s3; ord_tac1.
Qed.

Lemma iclosed_col_fs (p: property) (f := ordinalsf p) a
   (x:= f a) (y := f (succ_o a)) :
  (iclosed_collection p) -> (non_coll p) -> ordinalp a ->
  [/\ x <o y, p x, p y & (forall z, p z -> x <o z -> y <=o z)].
Proof.
move => [pa pb] pc oa.
set r := (fun x y => [/\ p x, p y & x <=o y]).
move: (ordinals_col_p1 pa pc); rewrite -/r -/f; move => [s1 s2 s3 s4 [s5 s6]].
move:(OS_succ oa) => ob.
move: (s4 _ _ (ord_succ_lt oa)) => h; split => //; try apply: s1 => //.
move => z /s2 [c oc -> fc].
by apply/(s5 (succ_o a) c ob oc) /ord_succ_ltP /(s6 _ _ oa oc).
Qed.

Lemma first_derivation_p f (fp := first_derivation f): normal_ofs f ->
  ( (forall x, ordinalp x -> f (fp x) = fp x) /\
    (forall y, ordinalp y -> f y = y -> exists2 x, ordinalp x & y = fp x)).
Proof.
move => pa.
move:(iclosed_fixpoints pa).
pose p z :=(ordinalp z /\ f z = z).
have sa: (forall x, p x -> ordinalp x) by move => x [].
rewrite /fp /first_derivation -/p.
move => [sc sd].
move:(ordinals_col_p1 sa sd) => [pb pc pd pe [pf pg]].
by split; [move => x /pb [_] | move => y oy fy; apply:pc ].
Qed.

Lemma first_derivation_p0 f: normal_ofs f ->
  normal_ofs (first_derivation f).
Proof.
by move /iclosed_fixpoints => [pa pb]; exact (ordinals_col_p2 pa pb).
Qed.

Lemma first_derivation_p1 f: normal_ofs f ->
  (first_derivation f \0o) = the_least_fixedpoint_ge f \0o.
Proof.
move => nf; move:(iclosed_fixpoints nf) => [pa pb].
set p := (fun z => ordinalp z /\ f z = z).
have pc: forall x, p x -> ordinalp x by move=> x [].
move: (ordinals_col_p1 pc pb) => [q1 q2 q3 q4 [q5 q6]].
rewrite /first_derivation -/p.
move: (normal_ofs_fix nf OS0) => [sa sb sc].
apply: ord_leA.
  move: (q2 _ (conj (proj32 sa) sb)) => [a oa ->]; apply: q3; ord_tac1.
move: (q1 _ OS0) => [/ozero_least ha hb]; exact (sc _ ha hb).
Qed.

Lemma first_derivation_p2 f: normal_ofs f -> f \0o <> \0o ->
  (first_derivation f \0o) = the_least_fixedpoint_ge f \1o.
Proof.
move => nf nf0; move:(iclosed_fixpoints nf) => [pa pb].
set p := (fun z : Set => ordinalp z /\ f z = z).
have pc: forall x, p x -> ordinalp x by move=> x [].
move: (ordinals_col_p1 pc pb) => [q1 q2 q3 q4 [q5 q6]].
rewrite /first_derivation -/p.
move: (normal_ofs_fix nf OS1) => [sa sb sc].
apply: ord_leA.
  move: (q2 _ (conj (proj32 sa) sb)) => [a oa ->]; apply: q3; ord_tac1.
move: (q1 _ OS0) => [/ozero_least ha hb]; apply: sc => //.
by apply: ord_ge1; [ ord_tac |move => ba; case: nf0; rewrite - ba].
Qed.

Lemma first_derivation_p3 f x: normal_ofs f -> ordinalp x ->
  (first_derivation f (succ_o x)) =
    the_least_fixedpoint_ge f (succ_o (first_derivation f x)).
Proof.
move => nf ox; move:(iclosed_fixpoints nf) => [pa pb].
set p := (fun z : Set => ordinalp z /\ f z = z).
have pc: forall y, p y -> ordinalp y by move=> y [].
move: (ordinals_col_p1 pc pb) => [q1 q2 q3 q4 [q5 q6]].
rewrite /first_derivation -/p.
move: (q1 _ ox) => [ta tb].
move: (normal_ofs_fix nf (OS_succ ta)) => [/ord_succ_ltP sa sb sc].
apply: ord_leA.
  move: (q2 _ (conj (proj32_1 sa) sb)) => [a oa av].
  case:(ord_le_to_el oa ox); first by move /q3; rewrite - av => l1; ord_tac.
  by rewrite av; move /ord_succ_ltP /q3.
move: (q1 _ (OS_succ ox)) => [ua ub];apply: sc => //; apply /ord_succ_ltP.
by apply: q4; apply: ord_succ_lt.
Qed.

Lemma normal_ofs_from_exten f g : f =1o g -> normal_ofs f -> normal_ofs g.
Proof.
move => h [sa sb].
split => //.
  move => x y xy; move: (xy)=> [[ox oy _]_].
 by rewrite - h // - h //; apply: sa.
move => x lx; move: (proj31 lx) => ox.
rewrite -(h x ox) (sb _ lx).
apply: ord_sup_2funI => t tx; apply: h; ord_tac.
Qed.

Lemma first_derivation_exten f g : f =1o g -> normal_ofs f ->
 first_derivation f =1o first_derivation g.
Proof.
move => h osf.
have osg := (normal_ofs_from_exten h osf).
move:(first_derivation_p osf) => [sa sb].
move:(first_derivation_p osg) => [sc sd].
move: (first_derivation_p0 osf) (first_derivation_p0 osg) => [ia _] [ib _].
apply: (sincr_ofs_exten ia ib).
move => x ox.
  have ha: ordinalp (first_derivation f x) by apply: ofs_sincr.
  by apply: sd => //;rewrite - h //;apply: sa.
move => x ox.
  have ha: ordinalp (first_derivation g x) by apply: ofs_sincr.
  by apply: sb => //;rewrite h //;apply: sc.
Qed.

Lemma first_derivation_p4 f C (E:= succ_c C) (f' := first_derivation f)
   (F := Zo E (fun z => f z = z)):
   infinite_c C ->
   normal_ofs f ->
   (forall x, inc x E -> inc (f x) E) ->
   order_isomorphism (Lf f' E F) (ordinal_o E) (graph_on ordinal_le F).
Proof.
move => iC nf fse;move: (iC) => [cC _].
move:(succ_c_pr1 cC); rewrite -/E; move=> [pa pb pc].
move: (iclosed_fixpoints nf)=> [pa' pb'].
move: (ordinals_col_p2 pa' pb') => nf'.
set p := (fun z : Set => ordinalp z /\ f z = z).
have pc': forall x, p x -> ordinalp x by move=> x [].
move: (ordinals_col_p1 pc' pb').
have -> : ordinalsf p = f' by [].
move => [q1 q2 q3 q4 [q5 q6]].
move: (proj1 pa) => oe.
have oes: forall x, inc x E -> ordinalp x by move => x xE; ord_tac.
set g := Lf f' E F.
have sa: forall a b, inc b E -> a <o b -> inc a E.
  move => a b /(ord_ltP oe) ha hb; apply/(ord_ltP oe); ord_tac.
have lfa: lf_axiom f' E F.
  move => x xE; move: (oes _ xE) => ox; move:(q1 _ ox) => [_ aa].
  apply: Zo_i => //; clear aa; ex_middle bad.
  pose P x := inc x E /\ ~ inc (f' x) E.
  have px: P x by [].
  move: (least_ordinal4 ox px); set z := least_ordinal _ _.
  move => [oz [zE ney] el]; case: ney.
  move:(oes _ zE) => ozz.
  have ell: forall t, t <o z -> inc (f' t) E.
    move => t tz; move: (proj31_1 tz) => ot.
    case: (p_or_not_p (P t)); first by move => /(el _ ot) => zt; ord_tac.
    move => pt; ex_middle ba;case: pt; split => //; apply: (sa _ _ zE tz).
  move: (next_fix_point_small iC nf fse) => H.
  case: (limit_ordinal_pr2 oz).
      move => ->; rewrite /f' (first_derivation_p1 nf); apply: H.
      apply succ_cP => //; split; rewrite ? (card_card CS0); fprops.
    move => [t ot te]; rewrite te.
    rewrite /f' (first_derivation_p3 nf ot); apply: H.
    by apply:(succ_c_succ iC); apply: ell; rewrite te; apply: ord_succ_lt.
  move => lz; move: (proj2 nf' _ lz).
  set w := ordinalsf _ ; have -> : w = f' by []; clear w.
  move => ->; apply: (succ_c_sup iC).
    move /(succ_cP cC): zE => [_]; apply: card_leT.
    exact: (fun_image_smaller z f').
  move => t /funI_P [u uz ->]; apply:ell; ord_tac.
split.
- apply: ordinal_o_or.
- apply: order_from_rel; apply:ordinal_le_order_r.
- split.
  + apply: lf_bijective.
   - done.
   - move => u v /oes ou /oes ov sv.
     by case: (ord_le_to_ell ou ov) => // /q4 []; rewrite sv.
   - move => y /Zo_P [ye yv]; move: (q2 _ (conj (oes _ ye) yv)) => [a a1 a2].
     exists a => //; move: (osi_gex q4 a1); rewrite - a2.
     move /(ord_ltP oe): ye => ha hb; apply/(ord_ltP oe); ord_tac.
- rewrite ordinal_o_sr /g; aw.
  rewrite graph_on_sr /g ; aw => a /Zo_P [/oes oa _]; ord_tac.
- red;rewrite /g;aw; move => x y xE yE; aw; split.
    move /sub_gleP => [_ _ xy]; apply /graph_on_P1; split; fprops.
    apply: q3; split; fprops.
 move /graph_on_P1 => [_ _] /(q5 _ _ (oes _ xE) (oes _ yE)) [_ _ xy].
 by apply /sub_gleP.
Qed.

Lemma ord_rev_pred x (y:= x -o \1o) : \0o <o x->
   (ordinalp y /\ x = \1o +o y).
Proof. by move /ord_ge1P => /odiff_pr. Qed.

Lemma rev_pred_prop (f := fun z => \1o +o z):
  normal_ofs f /\ (forall y, \0o <o y -> exists2 x, ordinalp x & y = f x).
Proof.
split; first apply: (osum_normal OS1).
by move => y /ord_rev_pred [pa pb]; exists (y -o \1o); rewrite /f.
Qed.

Lemma non_zero_ord_enum:
  (fun z => \1o +o z) =1o ordinalsf (fun x => \0o <o x).
Proof.
set p := (fun x => \0o <o x).
have pa :(iclosed_collection p).
  split; first by move => x [] h _; ord_tac.
  move => F hx [t tx].
  have osf: ordinal_set F by move => x /hx [h _]; ord_tac.
  move: (ord_sup_ub osf tx); apply: ord_lt_leT; exact (hx _ tx).
have pb: (non_coll p).
   rewrite /p;move=>[E xe].
   have ose: ordinal_set E by move => t /xe h; ord_tac.
   move: (OS_sup ose) => sse.
   move: (ord_lt0_succ (OS_sup ose)) => /xe se.
   by move:(ord_sup_ub ose se) => /ord_succ_ltP [].
move => t ot.
apply:(normal_ofs_uniqueness (p:= fun z => z+o \1o)).
- apply: (osum_normal OS1).
- by apply: (ordinals_col_p2 pa pb).
- by move => x ox; rewrite - (ord_succ_pr ox) (osumA OS1 ox OS1).
- move => x /(iclosed_col_fs pa pb).
  set u := ordinalsf p x; set v := ordinalsf p (succ_o x).
  rewrite /p;move =>[/ord_succ_ltP uv [[_ ou _] _] _ h].
  rewrite (ord_succ_pr ou); apply: ord_leA => //.
  apply: h; [by apply:ord_lt0_succ| by apply:ord_succ_lt].
- move: (iclosed_col_f0 pa pb); set u := ordinalsf p \0o.
  rewrite (osum0r OS1); move => [] /ord_ge1P u1 h.
  move: (h _ (ord_lt_01)) => h'; ord_tac.
- exact.
Qed.

Lemma add1_fix_enumeration:
  first_derivation (ord_sum2 \1o) =1o (ord_sum2 omega0).
Proof.
move: OS1 OS_omega => o1 oo.
move: (osum_normal o1) (osum_normal oo) => np1 npo.
move: (iclosed_fixpoints np1); move => [pa pb].
move:(ordinals_col_p2 pa pb) => na.
set (p:= fun z => ordinalp z /\ \1o +o z = z).
have pc: forall x, p x -> omega0 <=o x by move => x [p1 /(oadd1_fix p1)].
apply:(normal_ofs_uniqueness (p:= fun z => z+o \1o)) => //.
- move => x ox.
  move: (iclosed_col_fs pa pb ox).
  set u:= ordinalsf p x; set v := ordinalsf p (succ_o x).
  move => [sa /pc pu _ sc].
  have ou: ordinalp u by ord_tac.
  move: (ord_succ_lt ou) => h.
  rewrite ord_succ_pr //; apply: ord_leA; last by apply /ord_succ_ltP.
  apply: (sc _ _ h); split;[ fprops |apply:osum_1inf; move: h => [h _];ord_tac].
- move => x ox; rewrite - ord_succ_pr // osumA //.
- rewrite osum0r //.
  move: (iclosed_col_f0 pa pb); set fz := (ordinalsf p \0o). move => [sa sb].
  have /sb po: p omega0 by split => //; apply:osum_1inf; ord_tac.
  move: (pc _ sa) => e1; ord_tac.
Qed.

Lemma add_fix_enumeration a: ordinalp a ->
  first_derivation (ord_sum2 a) =1o (ord_sum2 (a *o omega0)).
Proof.
move => oa.
set b := a *o omega0.
have ob: ordinalp b by apply: (OS_prod2 oa OS_omega).
move: (osum_normal oa) (osum_normal ob) => npa npb.
move: (iclosed_fixpoints npa) => [pa pb].
move:(ordinals_col_p2 pa pb) => na.
apply:(normal_ofs_uniqueness (p:= fun z => z+o \1o)) => //.
    move => x ox.
    move: (iclosed_col_fs pa pb ox).
    set u:= ordinalsf _ x; set v := ordinalsf _ (succ_o x).
    move => [sa [ou sb] _ sc].
    move: (ord_succ_lt ou) => h.
    rewrite ord_succ_pr //; apply: ord_leA; last by apply /ord_succ_ltP.
    by apply: (sc _ _ h); split; [ fprops | rewrite - (osum2_succ oa ou) sb].
  move => x ox; rewrite - ord_succ_pr // osumA //; fprops.
rewrite osum0r //.
rewrite (first_derivation_p1 npa) /the_least_fixedpoint_ge.
move: (induction_defined_pr ([eta ord_sum2 a] ) \0o).
set g := induction_defined _ _; move => [sg [fg sjg g0 gn]].
rewrite sg in sjg.
move: BS0 => bs0.
have gnn: forall n, inc n Bnat -> Vf g n = a *o n.
  apply: cardinal_c_induction; first by rewrite oprod0r.
  move => n nb h.
  rewrite (gn _ nb) h (osum_x_xy oa (Bnat_oset nb)) (osum2_2int BS1 nb).
  by rewrite (Bsucc_rw nb) csumC.
case: (ord_zero_dichot oa) => ap.
  suff: (target g) = singleton \0o by move => ->; rewrite setU_1 /b ap oprod0l.
  apply: set1_pr; first by rewrite - g0; Wtac; rewrite sg.
  by move => z /sjg [x /gnn xsg <-]; rewrite xsg ap oprod0l.
move: (oprod_normal ap) => /normal_ofs_equiv1 [ha hb].
have ->: (target g) = (fun_image Bnat [eta ord_prod2 a]).
  set_extens t; first by move/sjg => [x xa <-]; apply /funI_P; ex_tac.
  move /funI_P => [z zB ->]; rewrite - (gnn _ zB); Wtac.
have bns:(forall x, inc x Bnat -> \0o <=o x) by move => x /Bnat_oset h;ord_tac1.
have nex: nonempty Bnat by ex_tac.
by rewrite(hb _ bns nex) - omega_limit0.
Qed.

Lemma omega_div x: ordinalp x ->
  exists a b, [/\ ordinalp a, b<o omega0, x = omega0 *o a +o b &
     (succ_op x <-> b <> \0o)].
Proof.
move => ox; move: (odivision_exists1 ox ord_lt_0omega) => [q [r ha]].
move: (ha) => [oq or xv ro].
exists q, r; split => //; split.
  move => [y oy sr] rz.
  move: (odivision_exists1 oy ord_lt_0omega) => [q' [r' [oq' or' xv' ro']]].
  have sr'p: succ_o r' <o omega0.
    by move /limit_ordinal_P3: omega_limit => [ _]; apply.
  have op: ordinalp (omega0 *o q') by fprops.
  have hb: ord_div_pr0 x omega0 q' (succ_o r').
     split => //; [ | rewrite -(osum2_succ op) - ? xv' //]; ord_tac.
  case: (@succo_nz r'); rewrite - rz.
  by move: (odivision_unique ox OS_omega ha hb) => [_].
move => rz.
have op: ordinalp (omega0 *o q) by fprops.
have rB: inc r Bnat by ord_tac.
move: (cpred_pr rB rz); set c := cpred r; move => [cB].
rewrite (succ_of_Bnat cB)=> rs; apply: (succ_op_pr ox).
by exists (omega0 *o q +o c);rewrite (osum2_succ op (Bnat_oset cB)) - rs.
Qed.

Lemma limit_ordinal_P4 x: ordinalp x ->
  (limit_ordinal x <-> exists2 y, \0o <o y & x = omega0 *o y).
Proof.
move => ox; split => h.
  move: (omega_div ox) => [a [b [oa ob dv etc]]].
  case: (equal_or_not b \0o) => bz.
    have xa: x = omega0 *o a by rewrite dv bz osum0r; fprops.
    case: (ord_zero_dichot oa) => az; last by exists a.
    by case: (limit_nz h); rewrite xa az oprod0r.
  move /etc: bz => sp.
  move: (osuccVidpred ox)=> [_]; case.
  by move /(limit_ordinal_P1 ox):h => [_ h1].
move: h => [y [[_ oy _] ynz] ->].
move: OS_omega omega_limit => oo ol.
case: (limit_ordinal_pr2 oy) => hx; first by case: ynz.
  move: hx => [z oz zv].
  rewrite zv (oprod2_succ oo oz); apply:osum_limit; fprops.
apply: oprod_limit => //; apply: ord_lt_0omega.
Qed.

Lemma non_succ_ord_enum:
  (fun z => omega0 *o z) =1o ordinalsf (fun x => ordinalp x /\ ~ (succ_op x)).
Proof.
set p := (fun x => ordinalp x /\ ~ (succ_op x)).
have p0: forall x, p x -> x = \0o \/ limit_ordinal x.
  move => x [/limit_ordinal_pr2 h pa]; case: h; fprops; done.
have p1: forall x, p x -> exists2 y, ordinalp y & x = omega0 *o y.
  move => x px; move: (px) => [ox _]; case: (p0 _ px).
    by move => ->; exists \0o; fprops; rewrite oprod0r.
  by move /(limit_ordinal_P4 ox) => [y [[_ oy _] _] yp]; exists y.
have p2: forall x, limit_ordinal x -> p x.
  move => x [ox xz xs]; split => // [] [n on ns].
  have /xs ha: inc n x by rewrite ns; fprops.
  by apply: (ordinal_irreflexive ox); rewrite {1} ns.
have pz: p \0o by split; fprops; move => [n on ns]; by case:(@succo_nz n).
have p3: forall x, ordinalp x -> p (omega0 *o x).
  move => x ox; case: (ord_zero_dichot ox); first by move => ->;rewrite oprod0r.
  by move => xz; apply: p2; apply /limit_ordinal_P4; fprops; exists x.
have opp: ordinal_prop p by move => x [].
have p4 :(iclosed_collection p).
  split => // F fl nef.
  have os: ordinal_set F by move => x /fl [].
  case: (ord_sup_inVlimit os nef); [by move => /fl | apply: p2].
move:ord_lt_0omega OS_omega => oo odo.
have p5: (non_coll p).
   apply:unbounded_non_coll => // x ox; exists(omega0 *o x); last by apply: p3.
   exact: (oprod_M1le oo ox).
apply:(normal_ofs_uniqueness (p:= fun z => z +o omega0)).
- apply: (oprod_normal oo).
- by apply: (ordinals_col_p2 p4 p5).
- by move => x ox; rewrite oprod2_succ.
- move => x /(iclosed_col_fs p4 p5).
  set u := ordinalsf p x; set v := ordinalsf p (succ_o x).
  move => [uv pu pv h]; move: uv h; move/p1:pu => [a oa ->].
  move/p1:pv => [b ob ->] /(oprod_Meqltr oa ob odo) => lab h.
  rewrite - (oprod2_succ odo oa); ex_middle bad.
  have ra := (oprod_Meqlt (ord_succ_lt oa) oo).
  have rb := (p3 _ (OS_succ oa)).
  move: (oprod_Meqltr ob (OS_succ oa) odo (conj (h _ rb ra) bad)).
  move /ord_lt_succP => ba; ord_tac.
- move: (iclosed_col_f0 p4 p5); set u := ordinalsf p \0o.
  by rewrite oprod0r; move => [_ h]; move: (h _ pz) => /ord_le0.
Qed.

Indecomposable ordinals


Definition ord_indecomposable z :=
  \0o <o z /\
  (forall x y, x <o z -> y <o z -> x +o y <> z).

Lemma OS_indecomposable x: ord_indecomposable x -> ordinalp x.
Proof. move => [h _]; ord_tac. Qed.

Lemma indecomp_pr x a b:
  ordinalp a -> ordinalp b ->
  ord_indecomposable x -> a +o b = x -> (a = x \/ b = x).
Proof.
move => oa ob [_ oix] xs.
move: (osum_M0le oa ob)(osum_Mle0 oa ob).
rewrite xs => h1 h2.
case: (equal_or_not a x)=> lt2; first by left.
case: (equal_or_not b x)=> lt1; first by right.
by move: (oix _ _ (conj h2 lt2) (conj h1 lt1)).
Qed.

Lemma indecomp_one: ord_indecomposable \1o.
Proof.
split; [ by apply: ord_lt_01 | move=> x y xo yo].
rewrite (ord_lt1 xo) (ord_lt1 yo) (osum0l OS0); fprops.
Qed.

Lemma indecomp_omega: ord_indecomposable omega0.
Proof.
split; first by apply:ord_ne0_pos; [apply:OS_omega | apply: omega_nz ].
by move=> x y xf yf; move: (osum2_lt_omega xf yf) => [_].
Qed.

Lemma indecomp_example x: \0o <o x ->
  ~ (ord_indecomposable (succ_o x)).
Proof.
move=> [[_ ox _] xnz'] [_ h2].
have p1:= (ord_succ_lt ox).
have xnz : x <> \0o by fprops.
have p2: \1o <o (succ_o x) by move: (ord_ge1 ox xnz) => h; ord_tac.
case: (h2 _ _ p1 p2); rewrite ord_succ_pr //.
Qed.

Lemma indecomp_limit x: ord_indecomposable x ->
   x = \1o \/ limit_ordinal x.
Proof.
move => oix.
move: (OS_indecomposable oix) => ox.
case:(limit_ordinal_pr2 ox); last by move=> lx; right.
  by move: oix => [[_ pa] _] xe; case: pa.
move=> [y oy ysx]; left; rewrite ysx.
case: (equal_or_not y \0o) => ynz; first by rewrite ynz succ_o_zero.
by move: (indecomp_example (ord_ne0_pos oy ynz)); rewrite -ysx.
Qed.

Lemma limit_infinite1 x: limit_ordinal x -> omega0 <=o x.
Proof.
move=> ln; rewrite /ordinal_le; move: (limit_infinite ln).
move: OS_omega => oo; move: ln => [ox _ _].
by move /(omega_P1 ox); split.
Qed.

Lemma indecomp_omega1 x: ord_indecomposable x ->
  x = \1o \/ omega0 <=o x.
Proof.
by case /indecomp_limit => h; [ left | right; apply: limit_infinite1].
Qed.

Lemma indecomp_prop1 x y: ord_indecomposable x ->
  y <o x -> y +o x = x.
Proof.
move=> [_ oi] ltyx; move: (ltyx) =>[leyx _].
move: (odiff_pr leyx) => [so se].
case: (equal_or_not (x -o y) x) => h; first by rewrite h in se.
have ltxy: (x -o y) <o x.
  split => //;rewrite {2} se; apply: osum_M0le => //; ord_tac.
by case: (oi _ _ ltyx ltxy).
Qed.

Lemma cardinal_indecomposable x: infinite_c x ->
   ord_indecomposable x.
Proof.
move => xc; move: (proj1 xc) => cx.
split; first by exact:(ord_ne0_pos (proj1 cx) (infinite_nz xc)).
move => u v le1 le2 eq.
have ou: ordinalp u by ord_tac.
have ov: ordinalp v by ord_tac.
move: (f_equal cardinal eq); rewrite osum_cardinal // (card_card cx).
move => eq2; symmetry in eq2.
move: le1 => /(ordinal_cardinal_le2P cx ou) lt1.
move: le2 => /(ordinal_cardinal_le2P cx ov) [_ lt2].
move: (sum2_infinite2 (CS_cardinal v) xc lt1 eq2) => h; by case: lt2.
Qed.

Lemma cardinal_indecomposable1 x a : infinite_c x -> a <o x ->
  ((x -o a) = x /\ cardinal (x -s a) = x).
Proof.
move => icx ax; move: (icx) => [cx _].
move: (ax) => [[oa ox _] _].
move: (cardinal_indecomposable icx) => oix.
move: (indecomp_prop1 oix ax) => eq1.
move: (odiff_pr (proj1 ax)); rewrite - {2} eq1; move => [ob bv].
split; first by symmetry; exact:(osum2_simpl ox ob oa bv).
move /(ordinal_cardinal_le2P (proj1 icx) oa): ax; move: icx.
rewrite -{1 2 4} (card_card cx) => /infinite_setP; apply:infinite_compl.
Qed.

Lemma indecomp_prop2 x y z: x <o z -> y <o z -> ord_indecomposable z ->
    x +o y <o z.
Proof.
move => xz yz oiz.
have ox: ordinalp x by ord_tac.
have oy: ordinalp y by ord_tac.
have oz: ordinalp z by ord_tac.
have h: (x +o y) +o z = z.
  by rewrite - osumA // indecomp_prop1 // indecomp_prop1.
split; [rewrite -h; apply:osum_Mle0; fprops |exact: ((proj2 oiz) _ _ xz yz)].
Qed.

Lemma indecompP x: \0o <o x ->
  (ord_indecomposable x <-> (forall y, y <o x -> y +o x = x)).
Proof.
move => xp; split; first by move => /indecomp_prop1.
move=> h; split=> // a b lt1 [lt2 ne2].
rewrite - (h _ lt1) => h1; case: ne2.
apply: osum2_simpl h1 => //; ord_tac.
Qed.

Lemma indecomp_prodP x y: \1o <o x -> \0o <o y ->
  (ord_indecomposable x <-> ord_indecomposable (y *o x)).
Proof.
move=> cx1 cy0.
have ox: ordinalp x by ord_tac.
have oy: ordinalp y by ord_tac.
split; last first.
  move: (ord_lt_leT ord_lt_01 (proj1 cx1)) => cx0.
  move=> [ha hb]; split => //; move=> a b ax bx.
  move: (oprod_Meqlt ax cy0) => lt1.
  move: (oprod_Meqlt bx cy0) => lt2.
  have oa: ordinalp a by ord_tac.
  have ob: ordinalp b by ord_tac.
  move: (hb _ _ lt1 lt2); rewrite - osum_prodD //.
  by move=> h1; dneg h2;rewrite h2.
move=> xi.
move: (OS_prod2 oy ox) OS1 => op io1.
move: (ord_lt_leT ord_lt_01 (proj1 cx1)) => xp.
have xynz: \0o <o y *o x by apply: oprod2_pos.
apply/(indecompP xynz) => z ltz.
have oz: ordinalp z by move: ltz => [[oz _]_].
move: (odivision_exists oy ox ltz)=> [q [r [[oq or zv ltrq] ltry]]].
apply: ord_leA; last by apply: osum_M0le.
set t1 := (y *o x).
have ot1: ordinalp t1 by fprops.
have : z <=o ((y *o q) +o y).
  by rewrite zv;apply:osum_Mlele; [apply:ord_leR; fprops| move: ltrq => [ok _]].
rewrite - oprod2_succ // - ord_succ_pr//; set t2:= (y *o (q +o \1o)) => zt1.
have: (z +o t1) <=o (t2 +o t1) by apply: osum_Mlele => //;ord_tac.
have oq1: ordinalp (q +o \1o) by fprops.
rewrite /t2 {2 4} /t1 -(osum_prodD oq1 ox oy).
have ->: ((q +o \1o) +o x) = x => //.
have spec: q +o \1o <> x by move: xi=> [_]; apply.
move /(indecompP xp): xi; apply.
by split=> //; rewrite (ord_succ_pr oq); apply /ord_succ_ltP.
Qed.

Lemma indecomp_div x y: ord_indecomposable x ->
  y <> \0o -> y <o x ->
  exists z, [/\ ord_indecomposable z, ordinalp z & x = y *o z].
Proof.
move=> oix ynz ltyx.
move: (ltyx) => [[oy ox _] _].
have yp: \0o <o y by ord_tac1.
move: (odivision_exists1 ox yp) => [q [r [oq or xeq [lt1 _]]]].
symmetry in xeq.
case: (indecomp_pr (OS_prod2 oy oq) or oix xeq) => h; last first.
  move: lt1; rewrite h => lt1; ord_tac.
rewrite -h; exists q; split => //.
case: (ord_zero_dichot oq) => qz.
  by case:(proj2 (proj1 oix)); rewrite -h qz oprod0r.
case: (ord_one_dichot qz) => q1; first by rewrite q1; apply: indecomp_one.
apply/(indecomp_prodP q1 yp); ue.
Qed.

Lemma indecomp_prod2 a (b:= a *o omega0): \0o <o a ->
  [/\ ord_indecomposable b, a <o b &
  forall c, ord_indecomposable c -> a <o c -> b <=o c].
Proof.
move=> ap.
have oa: ordinalp a by ord_tac.
have o1: \1o <o omega0 by apply /olt_omegaP; fprops.
split; first by move /(indecomp_prodP o1 ap) : indecomp_omega.
  apply:(oprod_Meq1lt o1 ap).
have anz:a <> \0o by ord_tac1.
move=> c oic ac.
move: (indecomp_div oic anz ac) => [z [oiz oz xp]].
rewrite /b xp; apply: oprod_Mlele; first by ord_tac.
case: (indecomp_omega1 oiz) => // z1.
move: xp; rewrite z1 oprod1r// => ca.
by move: ac => [_]; rewrite ca.
Qed.

Lemma indecomp_prod3 a: \0o <o a ->
   (succ_o a) *o omega0 = a *o omega0.
Proof.
move=> ap.
have oa: ordinalp a by ord_tac.
move: (ord_lt0_succ oa) => sp.
move: (indecomp_prod2 ap) (indecomp_prod2 sp).
set (x := (succ_o a) *o omega0); set (y:= a *o omega0).
move => [p1 p2 p3] [q1 q2 q3].
apply: ord_leA; last by apply: (p3 _ q1);apply /ord_succ_ltP;move: q2=> [ok _].
apply: (q3 _ p1); split; first by apply /ord_succ_ltP.
by move=> h; move: (indecomp_example ap);rewrite h.
Qed.

Lemma indecomp_sup E:
  (forall x, inc x E -> ord_indecomposable x) ->
  (nonempty E) ->
  ord_indecomposable (\osup E).
Proof.
move => ai [w wE].
have osE: ordinal_set E by move => t /ai [h _]; ord_tac.
move: (ord_sup_ub osE wE); set x := \osup E => wx.
move: (ord_lt_leT (proj1 (ai _ wE)) wx) => xp.
split=> // a b /(ord_lt_sup osE)[a' a'E aa'] /(ord_lt_sup osE) [b' b'E bb'].
set z := Yo (a' <=o b') b' a'.
have [zE az bz]: [/\ inc z E, a <o z & b <o z].
   rewrite /z; Ytac cp1; first by split => //; ord_tac.
   case: (ord_le_to_ee (proj32_1 aa') (proj32_1 bb')) => //.
   move => ba'; split => //; ord_tac.
move => abx; move: (ord_sup_ub osE zE); rewrite -/x -abx => l1.
move: (indecomp_prop2 az bz (ai _ zE)) => l2; ord_tac.
Qed.

Lemma indecomp_sup1 x: \0o <o x ->
  exists y, [/\ ord_indecomposable y, y <=o x &
    forall z, ord_indecomposable z -> z <=o x -> z <=o y].
Proof.
move=> xp.
have ox: ordinalp x by ord_tac.
set E := Zo (succ_o x) ord_indecomposable.
have pa: (forall t, inc t E -> ord_indecomposable t) by move =>t /Zo_P [_].
have pb: nonempty E.
  exists \1o; apply:Zo_i; last by apply:indecomp_one.
  move /ord_lt_succ_succP: xp => /(ord_ltP (OS_succ ox)).
  by rewrite succ_o_zero.
have pc: (forall t, inc t E -> t <=o x) by move =>t /Zo_P [] /(ord_leP ox).
have osE: ordinal_set E by move => t /pc => h; ord_tac.
move: (ord_sup_ub osE)(OS_sup osE) (ord_ub_sup osE ox pc) => pe pf pg.
move: (indecomp_sup pa pb) => h; exists (\osup E); split => // z za zx.
by apply: pe; apply: Zo_i =>//; apply /(ord_leP ox).
Qed.

Lemma indecomp_closed_noncoll:
 (iclosed_collection ord_indecomposable) /\ (non_coll ord_indecomposable).
Proof.
move: indecomp_sup => h.
split; first by split; [by move => x [xp _]; ord_tac | exact].
move => [E ep].
have ose: ordinal_set E by move => x /ep [xp _]; ord_tac.
move: (OS_sup ose) => os; move: (OS_succ os)=> oy.
have yp: \0o <o succ_o (union E) by apply: ord_lt0_succ.
move: (indecomp_prod2 yp); set z := _ *o _; move => [sa sb sc].
move: (ord_lt_leT (ord_succ_lt os) (proj1 sb)) => lt1.
have ze: inc z E by apply /ep.
move:(ord_sup_ub ose ze) => le1; ord_tac.
Qed.

Definition by induction for the exponential


Definition ord_induction_sup (g: fterm2) x y f :=
  \osup (fun_image y (fun z => g (f z) x)).

Definition ord_induction_prop w0 g u f:=
   forall x, u <=o x ->
   ( f x \0o = w0 x /\
    (forall y, \0o <o y -> f x y = ord_induction_sup g x y (f x))).

Lemma ord_induction_unique w0 g u f f':
   ord_induction_prop w0 g u f -> ord_induction_prop w0 g u f' ->
   forall x, u <=o x -> f x =1o f' x.
Proof.
move => pa pb x ux y0 oy0.
move: (pa _ ux) (pb _ ux) => [h1 h2] [h3 h4].
apply: (least_ordinal2 (p := fun y => f x y = f' x y)) oy0 => y oy lpy.
case: (ord_zero_dichot oy) => ey0; first by rewrite ey0 h1//.
rewrite (h2 _ ey0)(h4 _ ey0).
by apply: ord_sup_2funI => z /(ord_ltP oy) /lpy ->.
Qed.

Definition ord_induction_p w0 g x f :=
  (Yo (source f = \0o) (w0 x)
      (ord_induction_sup g x (source f) (Vf f))).

Definition ord_induction_aux w0 g x a :=
  transfinite_defined (ordinal_o a) (ord_induction_p w0 g x).

Definition ord_induction_defined w0 g :=
  fun x y => Vf (ord_induction_aux w0 g x (succ_o y)) y.

Lemma ord_induction_p0 w0 g x a: ordinalp a ->
  let f := (ord_induction_aux w0 g x a) in
     [/\ function f, source f = a,
       (inc \0o a -> Vf f \0o = w0 x) &
       (forall y, inc y a -> \0o <o y ->
         Vf f y = ord_induction_sup g x y (Vf f))].
Proof.
move=> ob f.
pose p := (ord_induction_p w0 g x).
move: (transfinite_defined_pr p (ordinal_o_wor ob)).
rewrite /transfinite_def (ordinal_o_sr a) //.
have sh: forall c, inc c a -> source
  (restriction_to_segment (ordinal_o a) c
      (transfinite_defined (ordinal_o a) p)) = c.
  move=> c cb; rewrite /restriction_to_segment /restriction1; aw.
  apply: ordinal_segment => //.
move=> [[h1 _] h2 h3]; split => //.
  move=> zb;rewrite (h3 _ zb).
  set h := restriction_to_segment _ _ _.
  by rewrite /p /ord_induction_p Y_true //; apply: sh.
move => y xb; rewrite (h3 _ xb); move: (sh _ xb) => sh1 yp.
rewrite {1} /p /ord_induction_p sh1.
have ynz: y <> \0o by ord_tac1.
rewrite /ord_induction_sup; Ytac0; apply: ord_sup_2funI => z zy /=.
have lt1: glt (ordinal_o a) z y.
  apply /ordo_ltP => //; apply: (ordinal_transitive ob xb zy).
rewrite restriction1_V //; last by apply /segmentP.
by rewrite h2 - {2} (ordinal_o_sr a); apply: sub_segment.
Qed.

Lemma ord_induction_p1 w0 g x:
  ord_induction_defined w0 g x \0o = w0 x.
Proof.
set b:= succ_o \0o.
have zb: (inc \0o b) by rewrite /b /succ_o; fprops.
have ob: ordinalp b by apply: OS_succ; apply: OS0.
by move: (ord_induction_p0 w0 g x ob)=> [_ _ h1 _ ];apply: h1.
Qed.

Lemma ord_induction_p2 w0 g x:
  ord_induction_defined w0 g x \1o = g (w0 x) x.
Proof.
set b:= succ_o \1o.
have zb: (inc \1o b) by rewrite /b /succ_o; fprops.
have ob: ordinalp b by apply: OS_succ; fprops.
move: (ord_induction_p0 w0 g x ob); set h := ord_induction_aux _ _ _ _.
move => [_ _ ha hb].
rewrite /ord_induction_defined (hb _ zb (ord_lt_01)) /ord_induction_sup.
by rewrite funI_set1 setU_1 ha //;apply: setU2_1; apply: set1_1.
Qed.

Lemma ord_induction_p3 w0 g a b1 b2 x:
  ordinalp b1 -> ordinalp b2 ->
  inc x b1 -> inc x b2 -> ordinalp x ->
  let f1 := (ord_induction_aux w0 g a b1) in
  let f2 := (ord_induction_aux w0 g a b2) in
     Vf f1 x = Vf f2 x.
Proof.
move=> ob1 ob2 xb1 xb2 ox f1 f2.
pose p z := inc z b1 -> inc z b2 -> Vf f1 z = Vf f2 z.
apply: (least_ordinal2 (p := p)) ox xb1 xb2 => z oz Hr zb1 zb2.
move: (ord_induction_p0 w0 g a ob1) => [_ _ h0 hg].
move: (ord_induction_p0 w0 g a ob2) => [_ _ h0' hg'].
case: (ord_zero_dichot oz) => ez0; first by rewrite ez0 /f1 /f2 h0 ?h0'//;ue.
rewrite (hg _ zb1 ez0) (hg' _ zb2 ez0); apply: ord_sup_2funI => t tz.
have tb1: inc t b1 by exact: (ordinal_transitive ob1 zb1 tz).
have tb2: inc t b2 by exact: (ordinal_transitive ob2 zb2 tz).
rewrite Hr //; ord_tac.
Qed.

Lemma ord_induction_exists w0 g u:
   ord_induction_prop w0 g u (ord_induction_defined w0 g).
Proof.
move => x ux;split; first by rewrite ord_induction_p1.
move=> y yp.
rewrite /ord_induction_defined.
have oy: ordinalp y by ord_tac.
have ob: ordinalp (succ_o y) by fprops.
have iyb: inc y (succ_o y) by rewrite /succ_o; fprops.
move: (ord_induction_p0 w0 g x ob) => [_ _ h0 hg].
rewrite (hg _ iyb yp); apply: ord_sup_2funI => t ty; congr(g _ x).
have ot: ordinalp t by ord_tac.
apply: ord_induction_p3 => //; fprops;rewrite /succ_o;fprops.
Qed.

Section OrdinalInduction.
Variables (u: Set) (w0: fterm) (f g : fterm2).
Hypothesis fv: f = ord_induction_defined w0 g.

Let w1 := fun x => (g (w0 x) x).
Definition OIax_w0 := forall x, u <=o x -> w0 x <o w1 x.
Definition OIax_w1 := forall x, u <=o x -> x <=o w1 x.
Definition OIax_g1 := forall x y, u <=o x -> u <=o y -> x <o (g x y).
Definition OIax_g2:= forall a b a' b',
    u <=o a -> u <=o b -> a <=o a' -> b <=o b' ->
   (g a b) <=o (g a' b').
Definition OIax_w2w:= forall a a', u <=o a -> a <=o a' -> (w0 a) <=o (w0 a').
Definition OIax_w2:= forall a a', u <=o a -> a <o a' -> w1 a <o w1 a'.
Definition OIax_w3:= forall a, u <=o a -> w1 a = a.
Definition OIax_g3:= forall a, u <=o a -> normal_ofs1 (fun b => g a b) u.
Definition OIax_g4:= forall a b c, u <=o a -> u <=o b -> u <=o c ->
     g (g a b) c = g a (g b c).

Definition OIax1 := [/\ OIax_w0, OIax_w1 & OIax_g1].
Definition OIax1b := OIax1 /\ OIax_g2.
Definition OIax2 := [/\ OIax1, OIax_g2, OIax_w2w & OIax_w2].
Definition OIax3 := [/\ OIax2, OIax_w3, OIax_g3 & OIax_g4].

Lemma ord_induction_p01 x: OIax1 -> u <=o x -> f x \0o <o f x \1o.
Proof.
by move => [h _ _] ux; rewrite fv ord_induction_p1 ord_induction_p2; apply:h.
Qed.

Lemma ord_induction_p4 x y: OIax1 ->
  u <=o x -> \0o <o y -> x <=o (f x y).
Proof.
move=> [_ aw1 ag1] ux0 loy0; ex_middle npy0.
move: (least_ordinal5 loy0 npy0 (p:= fun z => x <=o (f x z))) => /=.
set z := least_ordinal _ _; move=> [oz z1 npz lpy];case: npz.
move: (aw1 _ ux0) => le1.
case: (ord_one_dichot z1) => x3; first by rewrite x3 fv ord_induction_p2.
move: (ord_induction_exists w0 g ux0); rewrite -fv; move=> [r0 r00].
rewrite r00 // /ord_induction_sup {r0 r00}.
set T:= fun_image _ _.
have oT: ordinal_set T.
  move=> t /funI_P [w wc ->].
  have ow: ordinalp w by ord_tac.
  case: (ord_zero_dichot ow) => wz.
    rewrite wz fv ord_induction_p1; ord_tac.
  have /lpy le2: inc w (ordinal_interval \1o z).
    by apply /(ointv1 oz); split => //;ord_tac.
    move: (ag1 _ _ (ord_leT ux0 le2) ux0)=> le3; ord_tac.
have sT: inc (g (f x \1o) x) T by apply /funI_P; exists \1o=> //; ord_tac.
move: (ord_sup_ub oT sT) => r1.
have ww: x <=o f x \1o by rewrite fv ord_induction_p2; apply: (aw1 _ ux0).
exact: (ord_leT (ord_leT ww (proj1 (ag1 _ _ (ord_leT ux0 ww) ux0))) r1).
Qed.

Lemma ord_induction_p41 x y: OIax1 ->
  u <=o x -> \0o <o y -> u <=o (f x y).
Proof.
move=> ax1 ux y1; move: (ord_induction_p4 ax1 ux y1) => aux; ord_tac.
Qed.

Lemma ord_induction_p5 x y: OIax1 ->
  u <=o x -> ordinalp y -> ordinalp (f x y).
Proof.
move=> ax1 ux oy; case: (ord_zero_dichot oy) => y1.
  move: (ord_induction_p01 ax1 ux); rewrite y1 =>h; ord_tac.
move: (ord_induction_p4 ax1 ux y1) => h; ord_tac.
Qed.

Lemma ord_induction_p6 x y: OIax1 ->
  u <=o x -> ordinalp y -> ordinalp (g (f x y) x).
Proof.
move => ax1 ux oy; case: (ord_zero_dichot oy) => y1.
  move: (proj31 ax1 _ ux) => h; rewrite y1 fv ord_induction_p1; ord_tac.
move: (ord_induction_p41 ax1 ux y1) => h1.
move: (proj33 ax1 _ _ h1 ux)=> h2; ord_tac.
Qed.

Lemma ord_induction_p7 x y y': OIax1 ->
  u <=o x -> y <o y' -> g (f x y) x <=o (f x y').
Proof.
move=> ax1 ux yz.
have oy: ordinalp y by ord_tac.
have oy': ordinalp y' by ord_tac.
move: (ord_induction_exists w0 g ux); rewrite -fv; move=> [r0 r00].
have yp: \0o <o y' by exact: (ord_le_ltT (ozero_least oy) yz).
rewrite (r00 _ yp) /ord_induction_sup;set T:= fun_image _ _.
have oT: ordinal_set T.
  move => z /funI_P [v vz ->]; apply: ord_induction_p6 => //; ord_tac.
 apply: (ord_sup_ub oT); apply /funI_P; exists y => //; ord_tac.
Qed.

Lemma ord_induction_p8 x y y': OIax1 ->
  u <=o x -> y <o y' -> (f x y) <o (f x y').
Proof.
move=> ax1 ux yz.
wlog: y yz / \0o <o y => yp.
  case: (ord_zero_dichot (proj31_1 yz)) => y1; last by apply: yp.
  move: (ord_induction_p01 ax1 ux) => cp.
  case: (ord_one_dichot yz)=> y'1; first by rewrite y1 y'1; apply: cp.
  by move: (ord_le_ltT (proj1 cp) (yp _ y'1 ord_lt_01)); rewrite y1.
move: (ord_induction_p7 ax1 ux yz) => le1.
move: (ord_induction_p41 ax1 ux yp) => le2.
move: (proj33 ax1 _ _ le2 ux) => le3; ord_tac.
Qed.

Lemma ord_induction_p9 x y: OIax1 ->
  u <=o x -> ordinalp y -> y <=o (f x y).
Proof.
move=> ax1 ux.
apply: (least_ordinal2 (p:= fun z => z <=o f x z)) => t ot lpy.
case: (ord_zero_dichot ot).
  move => ->; move: (proj31_1 (ord_induction_p01 ax1 ux)) => h; ord_tac1.
move => tp.
have orf: ordinalp (f x t) by apply: ord_induction_p5.
case: (ord_le_to_el ot orf)=> // p3z.
move:(lpy _ p3z) (ord_induction_p8 ax1 ux p3z) => h1 h2; ord_tac.
Qed.

Lemma ord_induction_p10 x: OIax1 ->
  u <=o x -> normal_ofs (f x).
Proof.
move=> ax1 ux; split.
  move => y y'; apply:(ord_induction_p8 ax1 ux).
move => a la.
move: (ord_induction_exists w0 g ux); rewrite -fv;move=> [_ oag].
rewrite (oag _ (limit_positive la)) /ord_induction_sup.
apply: ord_sup_2cofinal; split => b /funI_P [z za ->].
  move: la => [pa pb pc]; move: (pc _ za) => sza.
  exists (f x (succ_o z)); first by apply/funI_P;ex_tac.
  apply: (ord_induction_p7 ax1 ux); apply: ord_succ_lt; ord_tac.
move: la => [pa pb pc]; have oz: ordinalp z by ord_tac.
exists (g (f x z) x); first by apply/funI_P; exists z.
case: (ord_zero_dichot oz) => aa.
  rewrite aa fv ord_induction_p1; exact (proj1 (proj31 ax1 _ ux)).
exact (proj1 (proj33 ax1 _ _ (ord_induction_p41 ax1 ux aa) ux)).
Qed.

Lemma ord_induction_p11 x b y y': OIax1 ->
  u <=o x -> ordinalp y -> ordinalp y' ->
    (f x y) <=o b -> b <o (f x (succ_o y)) ->
    (f x y') <=o b -> b <o (f x (succ_o y')) ->
    y = y'.
Proof.
move=> ax1 ux.
have h: sincr_ofs (f x) by move => t t';apply:(ord_induction_p8 ax1 ux).
apply: (sincr_bounded_unique h).
Qed.

Lemma ord_induction_p12 x b: OIax1 ->
  u <=o x -> (w0 x) <=o b ->
  exists y, [/\ y <=o b, (f x y) <=o b & b <o (f x (succ_o y))].
Proof.
move=> ax1 ux w1b.
have ob: ordinalp b by ord_tac.
case:(normal_ofs_bounded ob (ord_induction_p10 ax1 ux)).
   rewrite fv ord_induction_p1 => h; ord_tac.
move => [y [py pa pb]]; exists y; split => //.
exact (ord_leT (ord_induction_p9 ax1 ux py) pa).
Qed.

Lemma ord_induction_p13 x y: OIax1b ->
   u <=o x -> ordinalp y ->
    f x (succ_o y) = g (f x y) x.
Proof.
move=> [ax1 ax2] ux oy.
move: (ord_induction_exists w0 g ux); rewrite -fv; move => [_ ip].
have ysy: y <o succ_o y by apply: ord_succ_lt.
apply: ord_leA; last by apply: ord_induction_p7.
move: (OS_succ oy) => osy.
have syp: \0o <o succ_o y by apply /ord_lt_succP; apply: ozero_least.
rewrite (ip _ syp) /ord_induction_sup.
apply: ord_ub_sup.
- by move => t /funI_P [z zy ->]; apply:(ord_induction_p6 ax1 ux); ord_tac.
- by apply:(ord_induction_p6 ax1 ux oy).
- move => t /funI_P [s zsa ->].
  case /setU1_P: zsa; last first.
    by move => ->; apply:ord_leR; apply:ord_induction_p6.
  move => /(ord_ltP oy) => sy.
  have ox: (ordinalp x) by ord_tac.
  wlog: s sy / \0o <o s => sp.
    have [aux _]: g (f x \0c) x <o g (f x \1c) x.
      rewrite fv ord_induction_p1 ord_induction_p2.
      apply: (proj33 ax1 _ _ _ ux); exact: (ord_leT ux (proj32 ax1 _ ux)).
    case: (ord_zero_dichot (proj31_1 sy)); last by apply: sp.
    move => ->; case: (ord_one_dichot sy)=> yp; first by rewrite yp.
    apply: (ord_leT aux (sp _ yp ord_lt_01)).
move: (ord_induction_p41 ax1 ux sp) => pa.
apply: ax2 => //; [exact (proj1 (ord_induction_p8 ax1 ux sy))| ord_tac].
Qed.

Definition ord_induction_g_unit c :=
  [/\ ordinalp c , g c c = c, c = w0 c &
    forall x, u <=o x -> [/\ g x c = x, g c x = x & w0 x = c]].

Lemma ord_induction_zv c: ord_induction_g_unit c -> OIax1 ->
  [/\ (forall a, u <=o a -> (g (w0 a) a) = a),
      (forall a b, u <=o a -> ordinalp b ->
       f a (b +o \0o) = g (f a b) (f a \0o)),
   (forall a b, u <=o a -> ordinalp b ->
      f a (\0o +o b) = g (f a \0o) (f a b)),
   forall a b, u <=o a -> ordinalp b -> f a (b *o \0o) = f (f a b) \0o &
   forall a b, u <=o a -> ordinalp b -> f a (\0o *o b) = f (f a \0o) b].
Proof.
move => [oc gcc cwc h] ax1.
have pa: forall a, u <=o a -> f a \0o = c.
  by move => a au; rewrite fv ord_induction_p1 (proj33 (h _ au)).
split.
- by move => a au; move: (h _ au) => [h1 h2 ->].
- move => a b ua ob.
  rewrite (osum0r ob) (pa _ ua).
  case: (ord_zero_dichot ob); first by move => ->;rewrite (pa _ ua).
  by move => bp; move: (h _ (ord_induction_p41 ax1 ua bp)) => [ -> _ _].
- move => a b ua ob.
  rewrite (osum0l ob) (pa _ ua).
  case: (ord_zero_dichot ob); first by move => ->;rewrite (pa _ ua).
  by move => bp; move: (h _ (ord_induction_p41 ax1 ua bp)) => [ _ -> _].
- move => a b ua ob; rewrite oprod0r (pa _ ua).
  case: (ord_zero_dichot ob).
    by move => ->; rewrite (pa _ ua) fv ord_induction_p1 - cwc.
  by move => bp; rewrite (pa _ (ord_induction_p41 ax1 ua bp)).
- move => a b ua ob.
  rewrite oprod0l (pa _ ua) fv.
  move: (ord_induction_exists w0 g (ord_leR oc)).
  set gg := ord_induction_defined w0 g c; rewrite - cwc.
  rewrite /ord_induction_sup; move => [sa sb].
  apply:(least_ordinal2 (p:=fun z => c = gg z)) ob => y oy etc.
  case: (ord_zero_dichot oy); [ by move => -> | move => yz; rewrite (sb _ yz)].
  suff: (fun_image y (fun z => g (gg z) c)) = singleton c.
    by move => ->; rewrite setU_1.
  apply: set1_pr; first by apply /funI_P;exists \0o; [ord_tac | rewrite sa gcc].
  by move => z /funI_P [t /(ord_ltP oy) /etc <- ->].
Qed.

End OrdinalInduction.

Lemma ord_induction_p14
  (f:= ord_induction_defined id (fun u v:Set => succ_o u)):
  (forall a b, ordinalp a -> ordinalp b -> f a b = a +o b)
  /\ (forall a b c, ordinalp a -> ordinalp b -> ordinalp c ->
     f (f a b) c = f a (f b c)).
Proof.
pose w0 (x:Set) := x; set g := (fun u v:Set => succ_o u).
move: (erefl f) => ee.
have H: forall u, \0o <=o u -> u <o succ_o u.
  move => x [_ ox _]; exact (ord_succ_lt ox).
have ax1: OIax1 \0o w0 g.
  rewrite /w0 /g;split.
  - by move => x /H xp.
  - by move => x /H [].
  - by move => u v /H up.
have ax2:OIax_g2 \0o g by move => u v x y _ _ /ord_le_succ_succP uv _.
have aux: forall a, ordinalp a -> forall b, ordinalp b ->
   f a (succ_o b) = succ_o (f a b).
  move => a /ozero_least oa nb ob.
  rewrite (ord_induction_p13 ee (conj ax1 ax2) oa ob) //.
have f0: forall a, ordinalp a -> f a \0o = a +o \0o
   by move => a oa; rewrite /f (ord_induction_p1 w0 g a) osum0r.
split.
  move => a b oa ob.
  have ap: \0o <=o a by ord_tac1.
  move: (ord_induction_p10 ee ax1 ap) => nfa.
  have pa: (forall x, ordinalp x -> f a (succ_o x) = succ_o (f a x)).
    by move => x /(aux _ oa).
  have pb:(forall x, ordinalp x -> a +o succ_o x = succ_o (a +o x)).
    by move => x ox; rewrite osum2_succ.
  have pc: f a \0o = a +o \0o by apply: f0.
  move: (normal_ofs_uniqueness nfa (osum_normal oa) (p:=succ_o) pa pb pc).
  by apply.
move => a b c oa ob oc.
have ap: \0o <=o a by ord_tac1.
have bp: \0o <=o b by ord_tac1.
have hh: ordinalp (f a b) by apply:(ord_induction_p5 ee ax1 ap ob).
have qa: normal_ofs (fun c => f (f a b) c).
   apply: (ord_induction_p10 ee ax1); ord_tac1.
have qb: normal_ofs (fun c => f a (f b c)).
  by apply:normal_ofs_compose; apply: (ord_induction_p10 ee ax1).
have qc: forall x, ordinalp x -> f (f a b) (succ_o x) = succ_o (f (f a b) x).
  by move => x ox; rewrite aux.
have qd:forall x, ordinalp x -> f a (f b (succ_o x)) = succ_o (f a (f b x)).
  by move => x ox; rewrite aux // aux //; apply:(ord_induction_p5 ee ax1).
have qe: f (f a b) \0o = f a (f b \0o) by rewrite f0 // f0 // osum0r // osum0r.
exact: (normal_ofs_uniqueness qa qb qc qd qe oc).
Qed.

Lemma ord_induction_p15 a b:
  \0o <o a -> ordinalp b ->
  ord_induction_defined (fun z:Set=> \0o) ord_sum2 a b = a *o b.
Proof.
move => oa ob.
set w0 := (fun _ : Set => \0o).
have ax1: OIax1 \1o w0 ord_sum2.
  rewrite /w0;split.
  - move => x xp /=;rewrite osum0l; [ord_tac1 | ord_tac].
  - move => x [_ xp _] /=; rewrite osum0l //; ord_tac.
  - move => u v [_ ou _] /ord_ge1P vp.
    by move: (osum_Meqlt vp ou); rewrite osum0r.
have ax2:OIax_g2 \1o ord_sum2 by move => u v x y /= _ _; apply:osum_Mlele.
set f := (ord_induction_defined w0 ord_sum2).
have ap: \1o <=o a by ord_tac1.
move: (ord_induction_p10 (erefl f) ax1 ap) => nfa.
pose p := fun x => x +o a.
have pa: (forall x, ordinalp x -> f a (succ_o x) = p((f a x))).
  move => x ox; exact: (ord_induction_p13 (erefl f) (conj ax1 ax2) ap ox).
have pb:(forall x, ordinalp x -> a *o succ_o x = p (a *o x)).
  move => x ox; rewrite oprod2_succ //; ord_tac.
have pc: f a \0o = a *o \0o
  by rewrite /f (ord_induction_p1 w0 ord_sum2 a) oprod0r.
move: (normal_ofs_uniqueness nfa (oprod_normal oa) pa pb pc).
by apply.
Qed.

Section OrdinalInduction2.
Variables (u: Set) (w0: fterm) (f g : fterm2).
Hypothesis fv: f = ord_induction_defined w0 g.

Lemma ord_induction_p16 x y x' y': OIax2 u w0 g ->
  u <=o x -> x <=o x' -> y <=o y' -> f x y <=o f x' y'.
Proof.
move=> [ax1 ag2 aw2w aw2] ux xx' yy'.
have ux': u <=o x' by ord_tac.
have oy: ordinalp y by ord_tac.
apply: (@ord_leT _ (f x' y) _); last first.
  case: (equal_or_not y y') => nyy'.
    rewrite - nyy'; apply: ord_leR; apply: (ord_induction_p5 fv ax1 ux' oy).
  by move:(ord_induction_p8 fv ax1 ux' (conj yy' nyy')) => [].
apply:(least_ordinal2 (p:= fun y=> (f x y) <=o (f x' y))) oy => t ot lpy.
case: (ord_zero_dichot ot) => tp.
  by rewrite tp fv ord_induction_p1 ord_induction_p1; apply:aw2w.
move: (ord_induction_exists w0 g ux) (ord_induction_exists w0 g ux').
move => [_ aa] [_ bb]; rewrite fv (aa _ tp) (bb _ tp); clear aa bb.
rewrite /ord_induction_sup.
set T1:= fun_image _ _; set T2:= fun_image _ _.
have oT1: ordinal_set T1.
  move=> v => /funI_P [w /(ordinal_hi ot) ow ->].
  rewrite - fv;apply:(ord_induction_p6 fv ax1 ux ow).
have oT2: ordinal_set T2.
  move=> v => /funI_P [w /(ordinal_hi ot) ow ->].
  rewrite - fv;apply:(ord_induction_p6 fv ax1 ux' ow).
move: (OS_sup oT2) => osT2.
apply: (ord_ub_sup oT1 osT2).
move=> i => /funI_P [z /(ord_ltP ot) zt ->]; rewrite - fv.
have z2: inc z t by ord_tac.
have oz: ordinalp z by ord_tac.
move: (lpy _ zt) => h1.
set j :=g (f x' z) x'.
have jT2: inc j T2 by apply /funI_P ;exists z => //; rewrite - fv.
apply:(ord_leT _ (ord_sup_ub oT2 jT2)).
case: (equal_or_not x x') => exx'.
  by rewrite /j - exx'; apply:ord_leR; apply: (ord_induction_p6 fv ax1 ux).
case: (ord_zero_dichot oz) => zz.
 rewrite /j zz fv ! ord_induction_p1; exact(proj1(aw2 _ _ ux (conj xx' exx'))).
by apply: ag2=> //; apply: (ord_induction_p41 fv ax1).
Qed.

Lemma ord_induction_p17 x x' y: OIax2 u w0 g ->
   (forall a b b', u <=o a -> u <=o b -> b <o b' -> g a b <o g a b') ->
   u <=o x -> x <o x' -> ordinalp y ->
   f x (succ_o y) <o f x' (succ_o y).
Proof.
move=> ax2 gsi ux xx' oy.
have lexx' :x <=o x' by move:xx' => [xx' _].
have lexx: x <=o x by apply: ord_leR; ord_tac.
have ux': u <=o x' by ord_tac.
have le1: (f x y) <=o (f x' y) by apply: (ord_induction_p16)=> //; ord_tac.
move: (ax2) => [ax1 ag2 aw2w aw2].
case: (equal_or_not y \0o) => yz.
  rewrite yz succ_o_zero.
  rewrite fv ! ord_induction_p2;apply: aw2=> //.
have y1: \0o <o y by ord_tac1.
move: (ord_induction_p41 fv ax1 ux y1)=> h3.
have le2: (g (f x y) x) <=o (g (f x' y) x) by apply: ag2 =>//.
have l3: (g (f x' y) x) <o (g (f x' y) x') by apply: gsi=> //; ord_tac.
have le4: (g (f x y) x) <o (g (f x' y) x') by ord_tac.
rewrite (ord_induction_p13 fv (conj ax1 ag2) ux oy).
by rewrite (ord_induction_p13 fv (conj ax1 ag2) ux' oy).
Qed.

Lemma ord_induction_p18 a b c: OIax3 u w0 g ->
  u <=o a -> \0o <o b -> \0o <o c ->
    f a (b +o c) = g (f a b) (f a c).
Proof.
move => [[ax1 ag2 aw2w aw2] aw3 ag3 ag4] ua b1 /ord_ge1P c1.
pose p y := g y a.
pose F1 c := f a (b +o c).
pose F2 c := g (f a b) (f a c).
have ufab : u <=o f a b by apply: (ord_induction_p41 fv ax1).
have ob: ordinalp b by ord_tac.
set ax3:= conj ax1 ag2.
move: (ord_induction_p10 fv ax1 ua) => na.
have pu: (forall x, \1o <=o x -> F2 (succ_o x) = p (F2 x)).
  move => x /ord_ge1P xp.
  rewrite /F2 (ord_induction_p13 fv ax3 ua (proj32_1 xp)) /p ag4 //.
  by apply: (ord_induction_p41 fv ax1).
have pv: (forall x, \1o <=o x -> F1 (succ_o x) = p (F1 x)).
  move => x [_ ox _]; rewrite /F1 - (osum2_succ ob ox).
 rewrite (ord_induction_p13 fv ax3 ua) //; fprops.
have pa: normal_ofs1 F1 \1o.
  move: na => /normal_ofs_equiv1 n1.
  have bp: \0o <=o b +o \1o by apply: ozero_least; fprops.
  apply: (normal_ofs_compose1 OS0 OS1 bp n1).
  apply: (normal_ofs_equiv2 OS1); by apply: osum_normal.
have pb: normal_ofs1 F2 \1o.
  move: (ag3 _ ufab) => ea.
  have uf1 : u <=o f a \1o by apply:(ord_induction_p41 fv ax1 ua ord_lt_01).
  apply:(normal_ofs_compose1 (proj31 ua) OS1 uf1 ea).
  by apply: (normal_ofs_equiv2 OS1).
have pe: F1 \1o = F2 \1o.
  rewrite /F1 /F2 (ord_succ_pr ob) (ord_induction_p13 fv ax3 ua ob).
  by move: (aw3 _ ua); rewrite - (ord_induction_p2 w0 g a) - fv => ->.
exact:(normal_ofs_uniqueness1 pa pb OS1 pv pu pe c1).
Qed.

Lemma ord_induction_p19 a b c: OIax3 u w0 g ->
  u <=o a -> \0o <o b -> \0o <o c -> f a (b *o c) = f (f a b) c.
Proof.
move=> ax3 ua b1 /ord_ge1P c1.
move:(ax3) => [[ax1 ag2 aw2w aw2] aw3 ag3 ag4].
pose p y := g y (f a b).
pose F1 c := f a (b *o c).
pose F2 c := f (f a b) c.
have ufab : u <=o f a b by apply: (ord_induction_p41 fv ax1).
have ob: ordinalp b by ord_tac.
have pu: (forall x, \1o <=o x -> F2 (succ_o x) = p (F2 x)).
  by move=> x [_ xp _];rewrite /F2(ord_induction_p13 fv (conj ax1 ag2) ufab xp).
have pv: (forall x, \1o <=o x -> F1 (succ_o x) = p (F1 x)).
  move => x /ord_ge1P xp; rewrite /F1 (oprod2_succ ob (proj32_1 xp)).
  by rewrite ord_induction_p18 //; apply: oprod2_pos.
have pa: normal_ofs1 F1 \1o.
  have bp: \0o <=o b *o \1o by apply: ozero_least; apply: (OS_prod2 ob OS1).
  move: (ord_induction_p10 fv ax1 ua) => /normal_ofs_equiv1 n1.
  apply: (normal_ofs_compose1 OS0 OS1 bp n1); apply: (normal_ofs_equiv2 OS1).
  by apply: oprod_normal.
have pb: normal_ofs1 F2 \1o.
  apply: (normal_ofs_equiv2 OS1); apply:(ord_induction_p10 fv ax1 ufab).
have pe: F1 \1o = F2 \1o.
  by rewrite /F1 /F2 (oprod1r ob) {2} fv ord_induction_p2 (aw3 _ ufab).
exact:(normal_ofs_uniqueness1 pa pb OS1 pv pu pe c1).
Qed.

Lemma ord_induction_p21d x y: OIax1b u w0 g ->
   u <=o x -> \2o <=o y -> x <o (f x y).
Proof.
move=> ax1b ux y2; move: (ax1b) => [ax1 _].
have ox: ordinalp x by ord_tac.
move: (ord_induction_p4 fv ax1 ux ord_lt_01) => h.
move: (ord_le_ltT h (proj33 ax1 _ _ (ord_leT ux h) ux)).
rewrite -(ord_induction_p13 fv ax1b ux OS1) => h1; apply: (ord_lt_leT h1).
move: (proj32_1 h1);rewrite succ_o_one => h2.
case: (equal_or_not \2o y) => yn2; first by rewrite -yn2; ord_tac.
exact (proj1 (ord_induction_p8 fv ax1 ux (conj y2 yn2))).
Qed.

Lemma ord_induction_p21a x y: OIax1b u w0 g ->
   u <=o x -> y <o omega0->
   (g (w0 x) x) +o y <=o (f x (succ_o y)).
Proof.
move => ax22 ux; move /olt_omegaP; move: (ax22) => [ax1 ax2].
have ox: ordinalp x by ord_tac.
move: (proj32 (proj32 ax1 _ ux)) => ow.
move: y; apply: cardinal_c_induction.
  rewrite succ_o_zero osum0r //; rewrite fv ord_induction_p2; ord_tac.
move=> n nB hrec.
have ->: succ n = succ_o n.
    by move: nB => /BnatP aux; rewrite succ_of_finite //.
have on: ordinalp n by apply: OS_cardinal; fprops.
move: (ord_lt0_succ on) => le1.
rewrite (ord_induction_p13 fv ax22 ux);last by ord_tac.
move: (proj33 ax1 _ _ (ord_induction_p41 fv ax1 ux le1) ux) => le2.
rewrite - osum2_succ //; apply /ord_succ_ltP;ord_tac.
Qed.

Lemma ord_induction_p21b x: OIax1b u w0 g ->
  u <=o x -> x +o omega0 <=o (f x omega0).
Proof.
move=> ax ux; move: (ax) => [ax1 ax2].
have ox: ordinalp x by ord_tac.
move: OS_omega => oo.
move:omega_limit => ol.
rewrite (proj2 (ord_induction_p10 fv ax1 ux) _ ol)(proj2 (osum_normal ox) _ ol).
have ot: ordinal_set(fun_image omega0 (f x)).
   move => t /funI_P [w wi ->];apply: (ord_induction_p5 fv ax1) => //; ord_tac0.
apply: ord_ub_sup.
- move => t /funI_P [w wi ->];apply: OS_sum2 => //; ord_tac0.
- by apply:OS_sup.
- move => t /funI_P [w wi ->].
  move: (proj33 ol _ wi) => so.
  move/(ord_ltP oo):wi =>h; move: (ord_induction_p21a ax ux h) => l1.
  apply:(ord_leT (ord_leT(osum_Mleeq (proj32 ax1 _ ux) (proj31_1 h)) l1)).
  apply: ord_sup_ub => //; apply /funI_P; ex_tac.
Qed.

Lemma ord_induction_p21c x y: OIax1b u w0 g ->
  u <=o x -> omega0 <=o y -> x +o y <=o (f x y).
Proof.
move=> ax ux oy0; move: (ax) => [ax1 ax2].
move: ord_lt_0omega => loo.
pose p z := (omega0 <=o z -> (x +o z) <=o f x z).
have oy1: ordinalp y by ord_tac.
apply:(least_ordinal2 (p := p)) oy1 oy0 => z oy ply loy.
case: (equal_or_not omega0 z) => h1.
  by rewrite - h1; apply:ord_induction_p21b.
have ox: ordinalp x by ord_tac.
case: (limit_ordinal_pr2 oy) => H.
    move: (ord_lt_0omega); rewrite - H => h2; ord_tac.
  move:H=> [t ot tv]; rewrite tv.
  have lezo: omega0 <=o t by apply /ord_lt_succP; rewrite - tv.
  have lttz : t <o z by rewrite tv; apply: ord_succ_lt.
  have tp: \0o <o t by ord_tac.
  rewrite - osum2_succ //; apply /ord_succ_ltP.
  rewrite (ord_induction_p13 fv ax ux ot).
  move: (proj33 ax1 _ _ (ord_induction_p41 fv ax1 ux tp) ux).
  move: (ply _ lttz lezo) => l1 l2; ord_tac.
rewrite (proj2 (osum_normal ox) _ H).
set T1 := fun_image _ _.
have ot1: ordinal_set T1.
  move=> t /funI_P [w wi ->]; apply: OS_sum2 => //; ord_tac0.
have le1 : \0o <o z by ord_tac.
move: (ord_induction_p5 fv ax1 ux (proj31 H)) => ofo.
apply: (ord_ub_sup ot1 ofo) => i /funI_P [j jB ->].
have oj: ordinalp j by ord_tac.
have fi:forall t, t <o z -> f x t <=o f x z.
  move => t tz; exact (proj1 (ord_induction_p8 fv ax1 ux tz)).
case: (ord_le_to_el OS_omega oj) => cpjo.
  have jy: j <o z by apply /ord_ltP.
  by move: (ply _ jy cpjo) => h; apply: (ord_leT h); apply: fi.
have le3: (x +o j) <=o (x +o omega0) by apply: osum_Meqle => //; ord_tac.
apply: (ord_leT le3).
by apply: (ord_leT (ord_induction_p21b ax ux)); apply: fi.
Qed.

Definition critical_ordinal y :=
  [/\ ordinalp y, infinite_o y, u <o y &
   forall x, u <=o x -> x <o y -> f x y = y].

Lemma critical_limit y: OIax1b u w0 g -> critical_ordinal y -> limit_ordinal y.
Proof.
move=> ax [pa pb pc pd]; move: (ax) => [ax1 ax2].
case: (limit_ordinal_pr2 pa) => // h.
  move: pb => /(omega_P1 pa); rewrite h=> h1; move: BS0 => h2.
  move: (h1 _ h2); case; case.
move:h=> [z oz yv].
move: pc; rewrite yv ; move /ord_lt_succP => le1.
have lt1: z <o y by rewrite yv; apply /ord_lt_succP; ord_tac.
move: (pd _ le1 lt1).
rewrite yv (ord_induction_p13 fv ax le1 oz) => eq1.
have le3: \2o <=o z.
  apply /ord_lt_succP; rewrite - yv; apply / (ord_ltP pa).
  move: pb; move / (omega_P1 pa); apply;fprops.
have le2': \0o <o z by move: ord_lt_02 => h; ord_tac.
move: ((proj33 ax1) _ _ (ord_induction_p41 fv ax1 le1 le2') le1); rewrite eq1.
move / ord_lt_succP => l5.
move: (ord_induction_p21d ax le1 le3) => h; ord_tac.
Qed.

Lemma is_critical_pr y: OIax1b u w0 g ->
  omega0 <=o y -> u <o y ->
  (forall x, u <=o x -> x <o y -> f x y <=o y)
  -> critical_ordinal y.
Proof.
move=> ax p1 p2 p3; have oy: ordinalp y by ord_tac.
split => //; first by apply/ (omega_P1 oy); move: p1 => [_ _].
move => x ux xy; move: (p3 _ ux xy) => le1.
have ox: ordinalp x by ord_tac.
move: (ord_induction_p21c ax ux p1) (osum_M0le ox oy) => pa pb.
apply: (ord_leA le1); ord_tac.
Qed.

Lemma sup_critical A y: OIax2 u w0 g ->
   omega0 <=o y -> ordinal_set A -> \osup A = y ->
   (forall x, inc x A -> u <=o x) ->
   (forall x, inc x A -> f x y = y) ->
   critical_ordinal y.
Proof.
move=> ax2 ify osA sA uA hA.
move: (ax2) =>[ax1 ax0 _ _]; move: (conj ax1 ax0) => ax.
move: (OS_sup osA); rewrite sA => oy.
case: (emptyset_dichot A) => nea.
  move: sA; rewrite nea setU_0 -[emptyset]/ \0o.
  move: ord_lt_0omega => h1 h; rewrite h in h1; ord_tac.
have ly2: \2o <=o y.
  move: ord_lt_1omega => /ord_succ_ltP; rewrite succ_o_one => h; ord_tac.
have ly1: \1o <=o y by move: ord_lt_12 => [le1 _];ord_tac.
have xp1: forall x, inc x A -> x <o y.
  move => x xA; by move:(ord_induction_p21d ax (uA _ xA) ly2);rewrite (hA _ xA).
have uy: u <o y.
  move:nea => [x xA]; move: (uA _ xA)(xp1 _ xA) => ux xy; ord_tac.
apply: is_critical_pr => // x ux xy.
have ox: ordinalp x by ord_tac.
rewrite - sA in xy;move: (ord_lt_sup osA xy)=> [z zA [zl _]].
have leyy: y <=o y by ord_tac.
by move: (ord_induction_p16 ax2 ux zl leyy); rewrite (hA _ zA).
Qed.

Lemma sup_critical2: OIax2 u w0 g ->
  let A:= target (induction_defined0 (fun _ z => f z z) (u +o \2o)) in
  ordinalp u -> critical_ordinal (\osup A).
Proof.
move => ax; move: (ax) => [ax1 ax0 _ _]; move: (conj ax1 ax0) => ax1b.
move: (induction_defined_pr0 (fun _ z => f z z) (u +o \2o)) => /=.
set h:= induction_defined0 _ _; move=> [sh sjh h0 hrec] ou.
move: OS2 => o2.
have ou2: ordinalp (u +o \2o) by fprops.
have uu2: u <=o (u +o \2o) by apply: osum_Mle0.
have u2u: \2o <=o (u +o \2o) by apply: osum_M0le.
have pa: forall n, inc n Bnat ->
   [/\ ordinalp (Vf h n), u <=o (Vf h n), u +o \2o <=o (Vf h n),
   Vf h n <o Vf h (succ n) & n <o Vf h n].
  apply: cardinal_c_induction.
    have pa: \0o <o (u +o \2o) by move: ord_lt_02 => lt02; ord_tac.
    rewrite h0 //; split => //; [ ord_tac | rewrite (hrec _ BS0) h0].
    apply: ord_induction_p21d => //.
  move=> n nB [pa pb pc pd p]; rewrite (hrec _ nB).
  move: (ord_induction_p21d ax1b pb (ord_leT u2u pc)) => pf.
  move: (pf) => [pf1 _].
  have aux: \2o <=o Vf h n by ord_tac.
  split; try ord_tac.
     rewrite (hrec _ (BS_succ nB))(hrec _ nB).
     apply: ord_induction_p21d => //; ord_tac.
  apply: ord_le_ltT pf.
  by move: nB => /BnatP nb;rewrite succ_of_finite //; apply /ord_succ_ltP.
have pb: ordinal_set (target h).
   move=> t tt; move: ((proj2 sjh) _ tt); rewrite sh; move=> [x xs <-].
   by move: (pa _ xs) => [ok _].
move: (OS_sup pb) => os.
set y := union (target h).
move: (sjh) => [fcth _].
have zt: inc (u +o \2o) (target h).
  rewrite -h0; Wtac => //;rewrite sh; fprops.
have net: nonempty (target h) by ex_tac.
move: (ord_sup_ub pb zt); rewrite -/y => aux1.
move: (ord_leT u2u aux1) ord_lt_12 => qa qb.
move: (ord_lt_leT qb qa) => ly1y {aux1 qa qb}.
have pc: u <o y.
  move: (osum_Meqlt ord_lt_02 ou); rewrite (osum0r ou) => le1.
  move: (ord_sup_ub pb zt) => le2; ord_tac.
case: (ord_sup_inVlimit pb net) => yt.
  move: ((proj2 sjh) _ yt) => [v vsh vv].
  rewrite sh in vsh; move: (pa _ vsh) => [_ _ _ aux _].
  rewrite vv in aux.
  have pd:inc (Vf h (succ v)) (target h ) by Wtac; rewrite sh; fprops.
  move: (ord_sup_ub pb pd) => aux2; ord_tac.
apply: is_critical_pr => //.
  split => //; fprops; move => t tB; apply /(ord_ltP os).
  move: (pa _ tB)=> [_ _ _ _ lt1]; apply: (ord_lt_leT lt1).
  apply ord_sup_ub => //; Wtac; rewrite sh; fprops.
move => x ux xy.
move: (proj32_1 pc) => oy.
move: (ord_induction_p10 fv (proj1 ax1b) ux) => [_ nf5].
rewrite (nf5 _ yt) -/y; apply: ord_ub_sup => //.
  move=> z /funI_P [t ty ->]; apply: (ord_induction_p5 fv ax1 ux); ord_tac.
move=> i /funI_P [j /(ord_ltP oy) jy ->].
have fi: forall n, inc n Bnat ->
   forall m, inc m Bnat -> Vf h m <=o Vf h (m +c n).
  apply: cardinal_c_induction.
     move=> m mB; rewrite (bsum0r mB); apply: ord_leR.
     apply: pb; Wtac.
  move=> n nB hr m mB; apply: (ord_leT (hr _ mB)).
  rewrite (csum_via_succ _ nB);move: (pa _ (BS_sum mB nB))=> [_ _ _ ? _].
  ord_tac.
move: (ord_lt_sup pb xy) => [n nt [un _]].
move: (ord_lt_sup pb jy) => [m mt [um _]].
move: ((proj2 sjh) _ nt)((proj2 sjh) _ mt)=> [N Nv u1] [M Mv u2].
rewrite sh in Nv Mv; move: (fi _ Nv _ Mv); rewrite u2.
move: (fi _ Mv _ Nv); rewrite u1 csumC=> le1 le2.
have le3: x <=o (Vf h (M +c N)) by ord_tac.
have le4: j <=o (Vf h (M +c N)) by ord_tac.
move: (ord_induction_p16 ax ux le3 le4) => pd; apply: (ord_leT pd).
rewrite - hrec;[ apply: ord_sup_ub;[| Wtac; rewrite sh] |]; fprops.
Qed.

Lemma sup_critical3 A: OIax2 u w0 g -> nonempty A ->
   (forall x, inc x A -> critical_ordinal x) ->
   critical_ordinal (\osup A).
Proof.
move => ax; move: (ax) => [ax1 ax0 _ _]; move: (conj ax1 ax0) => ax1b.
move => neA hA.
have oA: ordinal_set A by move=> x xA; move: (hA _ xA) => [].
case: (inc_or_not (union A) A) => nuaa; first by apply: hA.
case: (ord_sup_inVlimit oA neA) => ca; first by apply: hA.
have osA: ordinalp (\osup A) by apply: OS_sup.
move: neA => [t tA];move: (ord_sup_ub oA tA) => lty.
move: (hA _ tA) => [p1 p2 p3 p4].
have ot: omega0 <=o t.
  move: p2 => /(omega_P1 p1) h; split; fprops.
have ou: omega0 <=o union A by ord_tac.
apply: is_critical_pr => //; first by ord_tac.
move=> x ux xu.
rewrite (proj2 (ord_induction_p10 fv ax1 ux) _ ca); apply: ord_ub_sup => //.
 move => z /funI_P [v cp ->]; apply: (ord_induction_p5 fv ax1 ux); ord_tac.
move => i /funI_P [j ji ->].
have [z [z1 z2 z3]]: exists z, [/\ x <=o z, j <=o z & z <o union A].
  have ox: ordinalp x by ord_tac.
  have oj: ordinalp j by ord_tac.
  case: (ord_le_to_el ox oj) => le.
    exists j; split => //; ord_tac.
    exists x; split => //; ord_tac.
move /(ord_ltP osA): (z3) => z4.
move: ca => [_ _ h]; move: (h z z4); move /(ord_ltP osA) => h1.
move: (ord_lt_sup oA h1) => [v vA zv].
have oz: ordinalp z by ord_tac.
move: (ord_succ_lt oz) => lt1.
have lt2: z <o v by ord_tac.
have [le4 _]: j <o v by ord_tac.
have le3: u <=o z by ord_tac.
apply: (ord_leT (ord_induction_p16 ax ux z1 le4)).
by move:(hA _ vA)=> [_ _ _ H]; rewrite (H _ le3 lt2); apply: ord_sup_ub.
Qed.

Lemma critical_indecomposable y: OIax2 u w0 g ->
  critical_ordinal y -> ord_indecomposable y.
Proof.
move => ax; move: (ax) => [ax1 ax0 _ _]; move: (conj ax1 ax0) => ax1b.
move=> [y1 y2 y3 y4].
have yp: \0o <o y by move: (ozero_least (proj31_1 y3)) => h; ord_tac.
apply/(indecompP yp) => x xy.
have ox: ordinalp x by ord_tac.
have ou: ordinalp u by ord_tac.
apply: ord_leA; last by apply: osum_M0le.
have ooy: omega0 <=o y.
  move: y2 => /(omega_P1 y1) => h;split;fprops.
have pa: forall t, u <=o t -> t +o y <=o (f t y).
  by move => t tp; apply: ord_induction_p21c.
case: (ord_le_to_ee ox ou) => lexu.
  have leuu: u <=o u by ord_tac.
  rewrite - {2} (y4 _ leuu y3); apply: ord_leT (pa _ leuu).
  by apply: osum_Mleeq.
by rewrite - {2} (y4 _ lexu xy); apply: pa.
Qed.

End OrdinalInduction2.

Ordinal power


Definition ord_pow' := ord_induction_defined (fun z:Set => \1o) ord_prod2.
Definition ord_pow a b :=
  Yo (a = \0o)
     (Yo (b = \0o) \1o \0o)
     (Yo (a = \1o) \1o (ord_pow' a b)).
Notation "x ^o y" := (ord_pow x y) (at level 30).

Lemma ord_pow_axioms: OIax3 \2o (fun z:Set => \1o) ord_prod2.
Proof.
move: (ord_lt_12) (ord_lt_02) => l12 l02.
split; first split; first split.
- move => a a2 /=; rewrite oprod1l; ord_tac.
- move => a [_ a2 _] /=; rewrite oprod1l//;ord_tac.
- move => a b a2 b2; apply:oprod_Meq1lt; ord_tac.
- by move => a b a' b' la lb; apply:oprod_Mlele.
- move => _ _ _ _ /=;fprops.
- move => a b [_ oa _] ab /=; rewrite !oprod1l//; ord_tac.
- by move => a [_ oa _] /=; rewrite oprod1l.
- move => a b. apply: normal_ofs_equiv2; fprops; apply: oprod_normal; ord_tac.
- move => a b c oa ob oc; symmetry;apply:oprodA; ord_tac.
Qed.

Lemma opow00: \0o ^o \0o = \1o.
Proof. by rewrite /ord_pow; Ytac0; Ytac0. Qed.

Lemma opow0x x: x <> \0o -> \0o ^o x = \0o.
Proof. by move=> xnz; rewrite /ord_pow; Ytac0; Ytac0. Qed.

Lemma opow0x' x: \0o <o x -> \0o ^o x = \0o.
Proof. move => h; apply:opow0x; ord_tac1. Qed.

Lemma opow1x x: \1o ^o x = \1o.
Proof.
have ooz: (\1o <> \0o) by fprops.
by rewrite /ord_pow; Ytac0; Ytac0.
Qed.

Lemma opowx0 x: x ^o \0o = \1o.
Proof.
rewrite /ord_pow /ord_pow'.
Ytac xnz; [by Ytac0 | by Ytac xno => //; rewrite ord_induction_p1 ].
Qed.

Lemma opowx1 x: ordinalp x -> x ^o \1o = x.
Proof.
have zno: \1o <> \0o by fprops.
rewrite /ord_pow; Ytac xz; [by Ytac0 |Ytac xno => // ].
move => ox;rewrite /ord_pow' ord_induction_p2 oprod1l //.
Qed.

Lemma opow2x x y: \2o <=o x -> x ^o y = ord_pow' x y.
Proof.
move=> x2; rewrite /ord_pow.
by move: (ord2_trichotomy1 x2) => [p1 p2]; Ytac0; Ytac0.
Qed.

Lemma OS_pow x y: ordinalp x -> ordinalp y ->
  ordinalp (x ^o y).
Proof.
move=> ox oy.
case: (ord2_trichotomy ox).
- move=> xz; case: (equal_or_not y \0o) => yz.
    rewrite xz yz opow00; fprops.
  rewrite xz opow0x //; fprops.
- by move => xo; rewrite xo opow1x; fprops.
- move:(ord_pow_axioms) =>[ [ax1 _ _ _] _ _ _].
  move => xo; rewrite (opow2x _ xo).
  apply:(ord_induction_p5 (erefl ord_pow') ax1 xo oy).
Qed.

Hint Resolve OS_pow : fprops.

Lemma opow_normal x: \2o <=o x ->
  normal_ofs (fun y => x ^o y).
Proof.
move=> x2; move: ord_pow_axioms => [[ax1 ax2 _ _ ] _ _ _].
move: (ord_induction_p10 (erefl ord_pow') ax1 x2).
have op: forall y, x ^o y = ord_pow' x y by move=> y ; apply: opow2x.
move => [pa pb];split; first by move => u v; rewrite /= op op; apply: pa.
by move => a al; rewrite op (pb _ al); apply: ord_sup_2funI => u; rewrite op.
Qed.

Lemma opow_Meqle1 x y: \0o <o x -> \0o <o y -> x <=o (x ^o y).
Proof.
move=> /ord_ge1P x2 y1.
case: (equal_or_not \1o x) => o1.
  rewrite - o1 opow1x; apply: ord_leR; fprops.
have o3: \2c <=o x by apply /ord2_lt_P; split.
move: ord_pow_axioms => [ [ax1 _ _ _] _ _ _].
move: (ord_induction_p4 (erefl ord_pow') ax1 o3 y1);rewrite opow2x //.
Qed.

Lemma opow_Mspec x y: \2o <=o x ->
  ordinalp y -> y <=o (x ^o y).
Proof.
move=> x2 y1.
move: ord_pow_axioms => [ [ax1 _ _ _] _ _ _].
move: (ord_induction_p9 (erefl ord_pow') ax1 x2 y1); rewrite opow2x //.
Qed.

Lemma opow_Meqlt x y y': \2o <=o x ->
  y <o y' -> (x ^o y) <o (x ^o y').
Proof.
move=> x2 yy'.
move: ord_pow_axioms => [ [ax1 _ _ _] _ _ _].
move: (ord_induction_p8 (erefl ord_pow') ax1 x2 yy'); rewrite !opow2x //.
Qed.

Lemma opow_Meqltr a b c: \2o <=o a ->
  ordinalp b -> ordinalp c ->
  ((b <o c) <-> ( (a ^o b) <o (a ^o c))).
Proof.
move=> h ob oc.
split; first by apply: opow_Meqlt.
move=> aux; case: (ord_le_to_ell oc ob).
- by move=> cb; move: aux => [_]; rewrite cb.
- move=> h1;move: (opow_Meqlt h h1) => [h2 _]; ord_tac.
- done.
Qed.

Lemma opow_regular a b c: \2o <=o a ->
  ordinalp b -> ordinalp c -> a ^o b = a ^o c -> b = c.
Proof.
move=> h ob oc.
case: (ord_le_to_ell ob oc) => //h1; move: (opow_Meqlt h h1) => [_ h2] //.
by move=> h3; case: h2.
Qed.

Lemma opow_nz0 x y:
  ordinalp x -> ordinalp y -> x ^o y = \0o ->
  (x = \0o /\ y <> \0o).
Proof.
move=> ox oy pz.
case: (equal_or_not y \0o).
  move=> h; move: pz; rewrite h; rewrite opowx0 => h'.
  by case: (card1_nz).
move=> ynz; split => //.
case: (equal_or_not x \0o) =>// xnz.
have [xp yp]: (\0o <o x /\ \0o <o y) by split; ord_tac1.
move: (opow_Meqle1 xp yp); rewrite pz; apply: ord_le0.
Qed.

Lemma opow_pos a b: \0o <o a -> ordinalp b -> \0o <o (a ^o b).
Proof.
move=> ap ob; move: (ap) => [[_ oa _] anz].
apply: ord_ne0_pos; fprops => bad; case: anz.
by move: (opow_nz0 oa ob bad) => [-> _].
Qed.

Lemma opow2_pos a b: \2o <=o a -> ordinalp b -> \0o <o (a ^o b).
Proof. move /(ord_lt_leT (ord_lt_02)); apply: opow_pos. Qed.

Lemma omega_pow_pos x: ordinalp x -> \0o <o omega0 ^o x.
Proof. move => ox; apply: opow2_pos => //; apply: ord_le_2omega. Qed.

Lemma omega_pow_nz x: ordinalp x -> omega0 ^o x <> \0o.
Proof. move=> /omega_pow_pos h; ord_tac1. Qed.

Lemma opow_Mlele x x' y y':
  x <> \0o -> x <=o x' -> y <=o y' ->
  (x ^o y) <=o (x' ^o y').
Proof.
move=> xnz xx' yy'.
have ox: ordinalp x by ord_tac.
have ox': ordinalp x' by ord_tac.
have oy': ordinalp y' by ord_tac.
case: (ord2_trichotomy ox) => // x2.
   rewrite x2 opow1x; apply: ord_ge1; fprops => h.
   move: (opow_nz0 ox' oy' h) => [p1 p2].
   rewrite p1 in xx'; move: (ord_le0 xx') => xz; contradiction.
move: ord_pow_axioms => [ax1 _ _ _ ].
move: (ord_induction_p16 (erefl ord_pow') ax1 x2 xx' yy').
by rewrite - (opow2x _ x2) - (opow2x _ (ord_leT x2 xx')).
Qed.

Lemma opow_Mleeq x x' y:
  x <> \0o -> x <=o x' -> ordinalp y ->
  (x ^o y) <=o (x' ^o y).
Proof. move => xne le1 oy; apply: opow_Mlele => //; ord_tac. Qed.

Lemma opow_Meqle x y y':
  \0o <o x -> y <=o y' -> (x ^o y) <=o (x ^o y').
Proof.
move => [[_ ox _] xn] le1 ; apply: opow_Mlele;[ fprops | ord_tac | done].
Qed.

Lemma opow_eq_one x y : ordinalp x -> ordinalp y -> x ^o y = \1o ->
    (x = \1o \/ y = \0o).
Proof.
move => ox oy p1.
case: (ord_zero_dichot oy) => yp; [ by right | left].
case: (ord_zero_dichot ox) => xp; first by move: p1; rewrite xp opow0x'.
move:(opow_Meqle1 xp yp); rewrite p1 => h.
move /ord_ge1P: xp => h1; ord_tac.
Qed.

Lemma opow_sum a b c:
  ordinalp a -> ordinalp b -> ordinalp c ->
  a ^o (b +o c) = (a ^o b) *o (a ^o c).
Proof.
move=> oa ob oc.
case: (ord_zero_dichot ob) => bz.
  rewrite bz opowx0 (osum0l oc) (oprod1l) => //; fprops.
case: (ord_zero_dichot oc) => cz.
  rewrite cz opowx0 (osum0r ob) (oprod1r) => //; fprops.
case: (ord2_trichotomy oa) => az.
- rewrite az (opow0x' bz) (opow0x' cz) opow0x.
    rewrite oprod0r => //; fprops.
  move=> sz; move: (osum_Mle0 ob oc); rewrite sz => bz1; ord_tac.
- rewrite az (opow1x b) (opow1x c)(opow1x (b +o c)).
  rewrite oprod1r //; fprops.
- move: (ord_induction_p18 (erefl ord_pow') ord_pow_axioms az bz cz).
  rewrite (opow2x b az)(opow2x c az) (opow2x (b +o c) az) //.
Qed.

Lemma opow_succ x y: ordinalp x -> ordinalp y ->
  x ^o (succ_o y) = (x ^o y) *o x.
Proof.
by move=> ox oy;rewrite -(ord_succ_pr oy) (opow_sum ox oy OS1) opowx1.
Qed.

Lemma opow_prod: forall a b c,
  ordinalp a -> ordinalp b -> ordinalp c ->
  a ^o (b *o c) = (a ^o b) ^o c.
Proof.
move=> a b c oa ob oc.
case: (equal_or_not b \0o) => bz.
  by rewrite bz (oprod0l) (opowx0 a) (opow1x c).
case: (equal_or_not c \0o) => cz.
  by rewrite cz (oprod0r) !opowx0.
have b1: \0o <o b by ord_tac1.
have c1: \0o <o c by ord_tac1.
case: (ord2_trichotomy oa) => az.
    rewrite az (opow0x bz) (opow0x cz) opow0x //.
    by apply: oprod2_nz.
  rewrite az (opow1x b) (opow1x c)(opow1x (b *o c)) //.
move: (ord_induction_p19 (erefl ord_pow') ord_pow_axioms az b1 c1).
rewrite (opow2x b az)(opow2x (b *o c) az).
suff: (\2o <=o (ord_pow' a b)) by move => f2; rewrite (opow2x c f2) //.
apply: (ord_induction_p41 (erefl ord_pow') _ az b1).
by move: ord_pow_axioms => [[]].
Qed.

Lemma opow_2int a b: inc a Bnat -> inc b Bnat -> a ^o b = a ^c b.
Proof.
move=> aB bB;move: b bB.
move: (Bnat_oset aB) => oa.
apply: cardinal_c_induction; first by rewrite cpowx0 opowx0.
move=> n nB hrec.
rewrite (pow_succ _ nB) (succ_of_Bnat nB) (opow_succ oa (Bnat_oset nB)) hrec.
apply: (oprod2_2int); fprops.
Qed.

Lemma opow_2int1 a b: a <o omega0 -> b <o omega0 -> (a ^o b) <o omega0.
Proof.
move /olt_omegaP => aB /olt_omegaP bB; apply /olt_omegaP.
rewrite (opow_2int aB bB); fprops.
Qed.

Lemma succ_c_pow C x y:
  infinite_c C -> inc x (succ_c C) -> inc y (succ_c C)
  -> inc (x ^o y) (succ_c C).
Proof.
move => ha hx; move: (ha) => [cc _].
move /(succ_cP cc): (hx) => [ox cx] /(succ_cP cc) [oy cy].
have s1: inc \1o (succ_c C).
  by apply:succ_c_leomega => //; move: ord_lt_1omega => [].
case: (ord2_trichotomy ox).
    case: (equal_or_not y \0o); first by move => ->; rewrite opowx0.
    move => ynz ->; rewrite opow0x //.
    apply:succ_c_leomega => //; by apply: ozero_least; fprops.
  by move => ->; rewrite opow1x.
pose p y := cardinal y <=c C -> inc (x ^o y) (succ_c C).
move => x2;apply: (least_ordinal2 (p := p)) oy cy => z oz hz coz.
have aux: forall t, t<o z -> inc (x ^o t) (succ_c C).
  move => t sa; apply: (hz _ sa).
  move: (ordinal_cardinal_le1 (proj1 sa)) => cc1; co_tac.
case: (limit_ordinal_pr2 oz); first by move => ->; rewrite opowx0.
  move=> [t ot zt].
  have tz: t <o z by rewrite (ord_ltP oz) zt /succ_o; fprops.
  move: (aux _ tz) => ee.
  by rewrite zt (opow_succ ox ot); apply: succ_c_prod.
move => lz.
rewrite (proj2 (opow_normal x2) _ lz); apply: succ_c_sup => //.
   apply: card_leT coz; apply: fun_image_smaller.
by move => j /funI_P [k /(ord_ltP oz) kz ->];apply: aux.
Qed.

Lemma opow_countable x y:
   countable_ordinal x -> countable_ordinal y -> countable_ordinal (x ^o y).
Proof.
move: omega_infinitec aleph_oneP => h T.
by move => /T xa /T xn; apply /T;apply:succ_c_pow.
Qed.

Lemma opow_Mspec2 a b: ordinalp b ->
  \2o <=o a -> (a *o b) <=o (a ^o b).
Proof.
move=> ob oa2.
have oa: ordinalp a by ord_tac.
move: (ord2_trichotomy1 oa2) => [pa1 pa2].
have a1: \0o <o a by ord_tac1.
apply: (least_ordinal2 (p:=fun z => (a *o z) <=o (a ^o z))) ob => t ot lpy.
case: (limit_ordinal_pr2 ot).
    move => ->;rewrite opowx0 oprod0r; apply: proj1 ord_lt_01.
  move=> [y oy ty].
  rewrite ty (opow_succ oa oy) (oprod2_succ oa oy).
  case: (ord_zero_dichot oy) => ynz.
    rewrite ynz oprod0r opowx0 osum0l // oprod1l //; ord_tac.
  move: (OS_pow oa oy) => op.
  move: (oprod_Mlele (ord_leR op) oa2).
  rewrite (ord_double op)=> le1.
  have lpyc: y <o t by rewrite ty; apply: ord_succ_lt.
  move: (lpy _ lpyc)=> le2.
  have au: a <=o (a ^o y) by apply: opow_Meqle1.
  move: (osum_Mlele le2 au) => le3; ord_tac.
move=> tl.
rewrite (proj2 (oprod_normal a1) _ tl).
move: (OS_pow oa ot) => pato.
apply: ord_ub_sup => //.
  move=> u /funI_P [z /(ordinal_hi ot) zt ->]; fprops.
move=> i /funI_P [z /(ord_ltP ot) zt ->].
apply: (ord_leT (lpy _ zt));exact:(opow_Meqle a1 (proj1 zt)).
Qed.

Lemma opow_Momega_le a b:
   a <=o b -> (omega0 ^o a) <=o (omega0 ^o b).
Proof. move=> lab; apply: (opow_Meqle ord_lt_0omega lab). Qed.

Lemma opow_Momega_lt: forall a b,
   a <o b -> (omega0 ^o a) <o (omega0 ^o b).
Proof.
move=> a b ab; apply: opow_Meqlt => //; apply: ord_le_2omega.
Qed.

Lemma omega_log_p1 x: \0o <o x ->
   exists y, [/\ ordinalp y, omega0 ^o y <=o x & x <o omega0 ^o (succ_o y)].
Proof.
move => ox.
have x1: \1o <=o x by ord_tac1.
have o2:= ord_le_2omega.
move: ord_pow_axioms => [[ax1 _ _ _ ] _ _ _].
move: (ord_induction_p12 (erefl ord_pow') ax1 o2 x1) => [y []].
rewrite - (opow2x _ o2) -(opow2x _ o2) => sa sb sc.
exists y; split => //; ord_tac.
Qed.

Definition ord_ext_div_pr (a b x y z: Set) :=
  [/\ ordinalp x, ordinalp y, ordinalp z &
  [/\ b = ((a ^o x) *o y) +o z,
   z <o (a ^o x), y <o a & \0o <o y]].

Lemma ord_ext_div_unique a b x y z x' y' z':
  \2o <=o a -> ordinalp b ->
  ord_ext_div_pr a b x y z -> ord_ext_div_pr a b x' y' z' ->
  [/\ x=x', y=y' & z=z'].
Proof.
move=> pa ob.
have oa: ordinalp a by ord_tac.
have aux: forall x y z, ord_ext_div_pr a b x y z ->
  (a ^o x <=o b /\ b <o a ^o (succ_o x)).
  move => u v w [ou ov ow [-> r1 r2 r3]]; move: (OS_pow oa ou) => op; split.
    apply:(ord_leT (oprod_Mle1 op r3)); apply:(osum_Mle0 (OS_prod2 op ov) ow).
  apply:(ord_lt_leT (osum_Meqlt r1 (OS_prod2 op ov))).
  rewrite - (oprod2_succ op ov) (opow_succ oa ou); apply:oprod_Meqle => //.
  by apply /ord_succ_ltP.
move => p1 p2.
move: (aux _ _ _ p1) (aux _ _ _ p2) => [p3 p4][p5 p6].
move: p1 p2 => [ox oy oz [bv r1 r2 r3]][ox' oy' oz' [bv' r1' r2' r3']].
have sx: x = x'.
   move: (proj1 (opow_normal pa)) => h.
   apply: (sincr_bounded_unique h ox ox' p3 p4 p5 p6).
rewrite - sx in bv' r1'.
have p7 : (ord_div_pr0 b (a^o x) y z) by split.
have p8 : (ord_div_pr0 b (a^o x) y' z') by split.
by move: (odivision_unique ob (OS_pow oa ox) p7 p8) => [e1 e2].
Qed.

Lemma ord_ext_div_exists a b:
  \2o <=o a -> \0o <o b ->
  exists x y z, ord_ext_div_pr a b x y z.
Proof.
move=> a2 b1.
have oa: ordinalp a by ord_tac.
have ob: ordinalp b by ord_tac.
case: (ord_le_to_el oa ob); last first.
  move=> ba; exists \0o; exists b; exists \0o; red.
  rewrite (opowx0 a) (oprod1l ob)(osum0r ob).
  move: OS0 => o0; split => //; split => //;by apply ord_lt_01.
move=> ab.
have b11: \1o <=o b by ord_tac1.
move: ord_pow_axioms => [[ax1 _ _ _ ] _ _ _].
move: (ord_induction_p12 (erefl ord_pow') ax1 a2 b11) => [y].
rewrite -(opow2x y a2) -(opow2x (succ_o y) a2).
move=> [yb fyb fysb].
have oy: ordinalp y by ord_tac.
move: fysb; rewrite (opow_succ oa oy).
move:(OS_pow oa oy)=> oay ltb.
move: (odivision_exists oay oa ltb)=> [q [r [[oq or abq lt1] lt2]]].
exists y; exists q; exists r; split => //; split => //.
apply/ord_ne0_pos => // qz; move: abq; rewrite qz (oprod0r) (osum0l or).
move=> h; rewrite h in fyb; ord_tac.
Qed.

Lemma opow_rec_def a b: \2o <=o a -> ordinalp b ->
  a ^o b = unionb (Lg b (fun x =>
    fun_image( (a-s1 \0o)\times a ^o x) (fun p => (a^o x) *o (P p) +o (Q p))))
    +s1 \0o.
Proof.
move => a2 ob.
have oa: ordinalp a by ord_tac.
have oab:= OS_pow oa ob.
have ap: \0o <o a by move:(ord_lt_02) => h; ord_tac.
set_extens t => h; last first.
  apply /(ord_ltP oab);case /setU1_P:h; last first.
    by move => ->; apply: opow_pos.
  move => /setUb_P; bw; move => [x xa]; bw.
  have lxb : x <o b by ord_tac.
  have ox:= (proj31_1 lxb).
  have oax:= (OS_pow oa ox).
  move /funI_P => [p /setX_P [pp /setC1_P [ua unz] /(ord_ltP oax) vb] ->].
  have ou: ordinalp (P p) by ord_tac.
  move:ua => /(ord_ltP oa) /ord_succ_ltP ua.
  move:(osum_Meqlt vb (OS_prod2 oax ou)) => h; apply: (ord_lt_leT h).
  rewrite - (oprod2_succ oax ou).
  move:(oprod_Meqle ua oax); rewrite - (opow_succ oa ox).
  move => h1; apply: (ord_leT h1); apply: opow_Meqle => //.
  by apply/ord_succ_ltP.
have ot: ordinalp t by ord_tac.
apply/setU1_P;case:(ord_zero_dichot ot);[ by right | move => tnz ; left].
move: (ord_ext_div_exists a2 tnz) => [x [y [z [ox oy oz [tv r1 r2 r3]]]]].
have aux: inc x b.
  apply /(ord_ltP ob); case: (ord_le_to_el ob ox) => // bx.
  move: (ord_leT (opow_Meqle ap bx) (oprod_Mle1 (proj32_1 r1) r3))=> l1.
  move: (ord_leT l1 (osum_Mle0 (proj32 l1) oz)); rewrite - tv => l2.
  move /(ord_ltP oab):h => l3; ord_tac.
apply/setUb_P; bw; exists x => //; bw.
apply /funI_P; exists (J y z); aw; apply: setXp_i => //.
   apply /setC1_P; split; [ ord_tac |ord_tac1].
ord_tac.
Qed.

Lemma indecomp_prop3 x:
  ord_indecomposable x -> exists2 y, ordinalp y & x = omega0 ^o y.
Proof.
move=> oidx.
move:(ord_ext_div_exists (ord_le_2omega) (proj1 oidx)) => [a [b [c]]].
move => [oa ob oc [pa pb pc pd]].
symmetry in pa.
have op: ordinalp (omega0 ^o a) by ord_tac.
move: (ord_lt_leT pb (oprod_Mle1 op pd)) => lt1.
have op1: ordinalp (omega0 ^o a *o b) by ord_tac.
case: (indecomp_pr op1 oc oidx pa) => eq1; last first.
  by move: (proj2 (ord_lt_leT lt1 (osum_Mle0 op1 oc))); rewrite pa eq1.
have bN: inc b Bnat by ord_tac.
have bz: b <> \0o by ord_tac1.
move: (cpred_pr bN bz)=> []; set b1:= cpred b.
move => ra;rewrite (succ_of_Bnat ra) => rb.
move:(Bnat_oset ra) => ob1;move: (OS_prod2 op ob1) => h2.
move: eq1; rewrite rb (oprod2_succ op ob1) => h1.
case:(indecomp_pr (OS_prod2 op ob1) op oidx h1) => eq2.
  by case: (omega_pow_nz oa); move: (osum_a_ab h2 op); rewrite h1; apply.
by exists a.
Qed.

Lemma indecomp_prop4 y: ordinalp y ->
  ord_indecomposable (omega0 ^o y).
Proof.
apply: (least_ordinal2 (p:=fun y => ord_indecomposable (omega0 ^o y))).
move => z oz zle.
case:(limit_ordinal_pr2 oz)=> zl.
    rewrite zl opowx0; exact indecomp_one.
  move: zl => [x ox zv].
  rewrite zv (opow_succ OS_omega ox).
  apply/(indecomp_prodP ord_lt_1omega (omega_pow_pos ox));apply: indecomp_omega.
rewrite (proj2 (opow_normal (ord_le_2omega)) _ zl).
set E := (fun_image z [eta ord_pow omega0]).
have pa: (forall x, inc x E -> ord_indecomposable x).
  by move => x /funI_P [t /(ord_ltP oz) tz ->]; apply: zle.
apply:(indecomp_sup pa); exists \1o; apply/funI_P.
by exists \0o; [exact (proj32 zl) | rewrite opowx0 ].
Qed.

Lemma indecomp_limit2 n: \0o <o n -> limit_ordinal (omega0 ^o n).
Proof.
move => [[_ on _] nz].
case: (indecomp_limit (indecomp_prop4 on)) => // pa.
case:(opow_eq_one OS_omega on pa) => h; first by case: (proj2 ord_lt_1omega).
by case: nz.
Qed.

Lemma indecomp_enum:
  (fun z => omega0 ^o z) =1o ordinalsf ord_indecomposable.
Proof.
set p := ord_indecomposable.
have opp: ordinal_prop p by move => x [h _]; ord_tac.
have p4 :(iclosed_collection p) by split => // F fl nef; apply: indecomp_sup.
move:ord_le_2omega OS_omega => oo odo.
have p5: (non_coll p).
   apply:unbounded_non_coll => // x ox; exists(omega0 ^o x).
     apply: (opow_Mspec oo ox).
   by apply: indecomp_prop4.
apply:(normal_ofs_uniqueness (p:= fun z => z *o omega0)).
- apply: (opow_normal oo).
- by apply: (ordinals_col_p2 p4 p5).
- by move => x ox; rewrite opow_succ.
- move => x /(iclosed_col_fs p4 p5).
  set u := ordinalsf p x; set v := ordinalsf p (succ_o x).
  move => [uv pu pv h]; move: uv h; move/indecomp_prop3:pu => [a oa ->].
  move/indecomp_prop3:pv => [b ob ->] /(opow_Meqltr oo oa ob) => lab h.
  rewrite - (opow_succ odo oa); ex_middle bad.
  have ra := (opow_Meqlt oo (ord_succ_lt oa)).
  have rb := (indecomp_prop4 (OS_succ oa)).
  move /(opow_Meqltr oo ob (OS_succ oa)):(conj (h _ rb ra) bad).
  move /ord_lt_succP => ba; ord_tac.
- move: (iclosed_col_f0 p4 p5); set u := ordinalsf p \0o.
  rewrite opowx0; move => [[/ord_ge1P up _] h]; move: (h _ indecomp_one).
  move => h1; ord_tac.
Qed.

Lemma oprod_fix1 a y (x := a ^o omega0 *o y):
  \0o <o a -> ordinalp y -> a *o x = x.
Proof.
move => ap oy.
have oa: ordinalp a by ord_tac.
rewrite (oprodA oa (OS_pow oa OS_omega) oy) -{1} (opowx1 oa).
by rewrite - (opow_sum oa OS1 OS_omega) (osum_int_omega ord_lt_1omega).
Qed.

Lemma oprod_fix2 a x: \0o <o a -> ordinalp x -> a *o x = x ->
  exists2 y, ordinalp y & x = a ^o omega0 *o y.
Proof.
move => ap ox xp.
move: (odivision_exists1 ox (opow_pos ap OS_omega)) => [q [r [oq or xv rs]]].
have oa: ordinalp a by ord_tac.
have ofp: ordinalp (a ^o omega0 *o q) by apply: OS_prod2 => //; ord_tac.
move: (osum_prodD ofp or oa); rewrite -xv xp xv (oprod_fix1 ap oq) => eq1.
move:( osum2_simpl or (OS_prod2 oa or) ofp eq1) => rar.
have ran: forall n, inc n Bnat -> r = a ^o n *o r.
  apply: cardinal_c_induction; first by rewrite opowx0 oprod1l.
  move => n nB h; move:(Bnat_oset nB) => os.
  have ->: succ n = \1o +o n.
    by rewrite (osum2_2int BS1 nB) csumC Bsucc_rw.
  by rewrite (opow_sum oa OS1 os)(opowx1 oa) -(oprodA oa (OS_pow oa os) or) -h.
case:(equal_or_not r \0o) => rz; first by exists q => //; rewrite rz osum0r.
case: (ord_one_dichot ap) => a2.
  by move: rs rz; rewrite a2 opow1x; move /ord_lt1.
suff: a ^o omega0 <=o r by move => h; ord_tac.
move/ord2_lt_P : a2 => a2; rewrite(proj2 (opow_normal a2) _ omega_limit).
apply: ord_ub_sup => //.
  move => s /funI_P [z /Bnat_oset zi ->]; fprops.
move => w /funI_P [n nB ->].
rewrite (ran _ nB); move:(Bnat_oset nB) => os.
apply:oprod_Mle1; [ fprops | ord_tac1].
Qed.

Lemma mult_fix_enumeration a: \0o <o a ->
  first_derivation (ord_prod2 a) =1o (ord_prod2 (a ^o omega0)).
Proof.
move => ap.
set b := a ^o omega0.
have oa: ordinalp a by ord_tac.
have ob: ordinalp b by apply: (OS_pow oa OS_omega).
have bp: \0o <o b by apply: opow_pos => //; apply: OS_omega.
move: (oprod_normal ap) (oprod_normal bp) => npa npb.
move: (iclosed_fixpoints npa) => [pa pb].
move:(ordinals_col_p2 pa pb) => na.
have w0: first_derivation (ord_prod2 a) \0o = b *o \0o.
  rewrite oprod0r.
  have h: ordinalp \0o /\ a *o \0o = \0o by split; [ fprops| apply: oprod0r].
  by move: (iclosed_col_f0 pa pb) => [_ sc]; move: (sc _ h) => /ord_le0.
apply:(normal_ofs_uniqueness (p:= fun z => z+o b)) => //; last first.
  by move => x ox; apply: oprod2_succ.
move => x ox.
move: (iclosed_col_fs pa pb ox). rewrite /first_derivation.
set u:= ordinalsf _ x; set v := ordinalsf _ (succ_o x).
move => [sa [ou sb] [ov sc] sd].
move: (oprod_fix2 ap ou sb) => [y oy uv].
move: (oprod_fix2 ap ov sc) => [z oz vv].
move: (OS_succ oy) => osu.
move: (conj (OS_prod2 ob osu) (oprod_fix1 ap osu)) => ea.
move:(oprod_Meqlt (ord_succ_lt oy) bp); rewrite - uv => eb.
move: (sd _ ea eb); rewrite uv - (oprod2_succ ob oy) vv -/b => h1.
ex_middle h2.
rewrite uv vv in sa;move: (oprod_Meqltr oy oz ob sa) => /ord_succ_ltP l1.
move: (oprod_Meqltr oz osu ob (conj h1 h2)) => l2; ord_tac.
Qed.

repeated derivations

Lemma closed_cofinal_inter C S (E := succ_c C) (T:= intersectionb S):
  infinite_c C -> fgraph S -> ordinalp (domain S) ->
  cardinal (domain S) <=c C ->
  nonempty (domain S) ->
  (forall i, inc i (domain S) -> iclosed_set (Vg S i)) ->
  (forall i, inc i (domain S) -> ord_cofinal (Vg S i) E) ->
  (forall i j, i<o j -> inc i (domain S) -> inc j (domain S) ->
       sub (Vg S j) (Vg S i)) ->
  iclosed_set T /\ ord_cofinal T E.
Proof.
move => ic fgs ods cds neds h1 h2 h4.
move: (succ_c_pr1 (proj1 ic)) => [sa sb sc].
have pa: inc \0o (domain S) by move:(ord_ne_pos ods neds) => /(ord_ltP ods).
have h3: (forall i, inc i (domain S) -> sub (Vg S i) E) by move => i /h2 [].
have pb: sub T E by move => x h; move:(setIb_hi h pa); apply: h3.
move: (infinite_card_limit2(ge_infinite_infinite ic (proj1 sb))) => hy.
move: (hy) => [oe _ _].
have ucc:union (succ_c C) = E.
  by move /(limit_ordinal_P1 oe): hy => [_ <-].
have pc: forall i, inc i (domain S) -> \osup (Vg S i) = E.
   move => i ids; move /(ord_cofinal_p2 hy (h3 _ ids)): (h2 _ ids).
   by rewrite ucc.
have ose: ordinal_set E by move => t te; ord_tac.
have neS: nonempty S by apply/domain_set0P.
have pd: ord_cofinal T E.
  split => // x xe.
  pose yipp i y := [/\ inc y (Vg S i), x <=o y & forall z,
         inc z (Vg S i) -> x <=o z -> y <=o z].
  pose yi i := choose (yipp i).
  have yip:forall i, inc i (domain S) -> yipp i (yi i).
    move => i ids; apply: choose_pr; move: (pc _ ids) => h.
    have xe1 : x <o \osup (Vg S i) by rewrite h; move/(ord_ltP oe): xe.
    have osi: ordinal_set (Vg S i) by move => t /(h3 _ ids) /ose.
    move: (ord_lt_sup osi xe1) => [z za [zb _]].
    pose p z := inc z (Vg S i) /\ x <=o z.
    have pz: p z by split.
    have oz: ordinalp z by ord_tac.
    move: (least_ordinal4 oz pz) => [ta [tb tc] td].
    exists (least_ordinal p z); split => // t te tf; apply: td => //; ord_tac.
  have yi1: forall i j, inc i (domain S) -> inc j (domain S) -> i <o j ->
     inc (yi j) (Vg S i).
    move => i j ids jds ij; move: (h4 _ _ ij ids jds); apply.
    by move:(yip _ jds) => [].
  have yi2: forall i j, inc i (domain S) -> inc j (domain S) -> i <o j ->
     (yi i) <=o (yi j).
    move => i j ids jds ij; move: (yi1 _ _ ids jds ij) => h.
    move: (yip _ ids) (yip _ jds) => [_ _ uc] [ _ ub _]; apply: uc => //.
  set F := fun_image (domain S) yi.
  have cf: cardinal F <=c C.
    move: (fun_image_smaller (domain S) yi); rewrite -/F => h; co_tac.
  have fe: sub F E.
    move => t /funI_P [z zdf ->]; move /yip:(zdf) => [ua ub _].
    by apply: (h3 _ zdf).
  have osf: ordinal_set F by move => t /fe /ose.
  move: (succ_c_sup ic cf fe); rewrite -/E => pd.
  have pe: x <=o (union F).
    move:(yip _ pa) => [_ ua _]; apply: (ord_leT ua); apply: ord_sup_ub => //.
    apply /funI_P; ex_tac.
  exists (\osup F) => //; apply/setIb_P => //.
  move => i ids.
  set G := fun_image (Zo (domain S) (fun z => i <=o z)) yi.
  have pf:inc i (Zo (domain S) [eta ordinal_le i]).
      apply/Zo_P; split => //; apply: ord_leR; ord_tac.
  have eq1: \osup G = \osup F.
    apply:ord_sup_1cofinal => //; split.
       move => t /funI_P [z /Zo_P [ha _] ->]; apply/funI_P; ex_tac.
    move => a /funI_P [z za ->].
    have oi: ordinalp i by ord_tac.
    have oj: ordinalp z by ord_tac.
    case (ord_le_to_el oi oj) => h5.
       exists (yi z); first by apply/funI_P; exists z => //; apply/Zo_P => //.
       by apply: ord_leR; move: (yip _ za) => [_ h6 _]; ord_tac.
    by exists (yi i); [ apply/funI_P; exists i | apply:yi2 ].
  have g1: sub G (Vg S i).
    move => t /funI_P [j /Zo_P [ja jb] ->]; case (equal_or_not i j) => ij.
       by rewrite ij; move: (yip _ ja) => [h6 _ _].
    apply:yi1 => //.
  have neg: nonempty G by exists (yi i); apply/funI_P; exists i.
  move:(h1 _ ids) => [sd se]; move: (se _ g1 neg); rewrite eq1.
  case => //; move: (pc _ ids); move => -> h6.
  by move: pd; rewrite h6 => h7; case: (ordinal_irreflexive oe).
split => //.
have pe: \osup T = E by move /(ord_cofinal_p2 hy pb): pd ->.
split; first by move => x /pb /(succ_cP (proj1 ic)) [].
move => F FT neF; rewrite pe;set x := (\osup F).
case (equal_or_not x E) => xe; [by left | right ].
apply/setIb_P => // i ids; move:(h1 _ ids) => [_ h5].
have h6: sub F (Vg S i) by move => t /FT ts; exact :(setIb_hi ts ids).
move: (h5 _ h6 neF); rewrite (pc _ ids); rewrite - /x; case => //.
Qed.

Definition many_der_aux f E g :=
  Yo (source g = \0o) f
      (ordinalsE E (intersectionf (source g) (fun z => fixpoints (Vf g z)))).

Definition many_der f E :=
  transfinite_defined (ordinal_o E) (many_der_aux f E).

Lemma many_der_ex C (E := succ_c C) f (g:= many_der f E)
  (ii:= fun i => (intersectionf i (fun z => fixpoints (Vf g z)))):
  infinite_c C -> normal_function f E E ->
  [/\ function g, source g = E, Vf g \0o = f,
      (forall i, inc i E -> i <> \0o -> Vf g i = ordinalsE E (ii i)) &
       [/\ forall i, inc i E -> i <> \0o ->
          lf_axiom (ordinals (fun x y => [/\ inc x (ii i), inc y (ii i)
        & x <=o y])) E E,
       (forall i, inc i E -> i <> \0o ->
          iclosed_set (ii i) /\ ord_cofinal (ii i) E),
       (forall i, inc i E -> normal_function (Vf g i) E E) &
       (forall i, inc i E -> i <> \0o ->
         image_of_fun (Vf g i) = (ii i))]].
Proof.
move => pa pb.
have oe: ordinalp E by move:(succ_c_pr1 (proj1 pa)) => [[]].
move:(transfinite_defined_pr (many_der_aux f E) (ordinal_o_wor oe)).
move => [];rewrite -/(many_der _ _) -/g ordinal_o_sr => sa sb sc.
have ze: inc \0o E by apply:succ_c_leomega => //; apply:ozero_least; fprops.
have fg: function g by fct_tac.
move: (sc _ ze).
  rewrite /many_der_aux /restriction_to_segment /restriction1; aw.
  have -> :(segment (ordinal_o E) \0o = \0o) by rewrite ordinal_segment.
  Ytac0 => pc.
pose fpi i := fixpoints (Vf g i).
have pd: forall i, inc i E -> i <> \0o -> Vf g i = ordinalsE E (ii i).
  move => i ie inz; move: (sc _ ie).
  rewrite /many_der_aux /restriction_to_segment {1 2} /restriction1; aw.
  have -> :(segment (ordinal_o E) i = i) by rewrite ordinal_segment.
  Ytac0 ; move => ->; congr (fun z => ordinalsE E z); apply: setIf_exten.
  move => k ki /=; rewrite restriction1_V // sb.
  apply: (ordinal_transitive oe ie).
split => //.
pose pp k := iclosed_set (fpi k)/\ ord_cofinal (fpi k) E /\
   forall l, l <o k -> sub (fpi k) (fpi l).
have pe: forall i, inc i E -> i<> \0o -> (forall k, k <o i -> pp k) ->
  iclosed_set (ii i) /\ ord_cofinal (ii i) E.
  move => i ie inz H.
  set S := Lg i fpi.
  have qa: fgraph S by rewrite /S; fprops.
  have qb: domain S = i by rewrite /S; bw.
  move /(succ_cP (proj1 pa)): ie => [u1 u2].
  have qc: ordinalp (domain S) by rewrite qb.
  have qd: cardinal (domain S) <=c C by rewrite qb.
  have qe: nonempty (domain S) by exists \0o; rewrite qb; apply:ord_ne_has0.
  have qf:(intersectionb S) = ii i.
    rewrite /ii /S /fpi /intersectionb; bw;apply:setIf_exten => k kd; bw.
  move:( closed_cofinal_inter pa qa qc qd qe); rewrite -/E qf qb; apply.
  + move => k ki; rewrite /S; bw.
    have /H [ha _] //: k <o i by ord_tac.
  + move => k ki;rewrite /S; bw.
    have /H [_ [ha _]] //: k <o i by ord_tac.
  + move => k j kj ki ji; rewrite /S; bw.
    have /H [_ [ _ hc]]: j <o i by ord_tac.
    exact: (hc _ kj).
have pf: forall i, inc i E -> i <> \0o ->
    iclosed_set (ii i) /\ ord_cofinal (ii i) E ->
    normal_function (Vf g i) E E /\ image_of_fun (Vf g i) = ii i.
  move => i ie inz [ta tb]; move: (ordinals_set_normal1 pa ta tb).
  by have -> //: ordinalsE E (ii i) = Vf g i; rewrite pd => //; move => [_].
have pg: forall k, inc k E -> pp k.
  move =>k ke.
  have ok: ordinalp k by ord_tac.
  apply: (least_ordinal2 (p:= fun z => inc z E -> pp z)) ok ke => l ol etc le.
  have ss: forall i, inc i E -> (source (Vf g i)) = E.
    move => i ie; case (equal_or_not i \0o) => iz.
      by rewrite iz pc; move: pb => [[]].
    rewrite (pd i ie iz) /ordinalsE; aw.
  case (equal_or_not l \0o) => l0.
     have sfe: source f = E by move: pb => [ [] ].
     rewrite l0 /pp/fpi pc; split.
    apply:(iclosed_fixpoints_fun oe) => //; rewrite sfe.
     split; last by move => m /ord_lt0.
     by apply: normal_fix_cofinal.
  have ww: forall k, k <o l -> pp k.
    move /(ord_ltP oe): le => lte.
    move => t tl; apply: etc => //;apply/(ord_ltP oe); ord_tac.
  move: (pe _ le l0 ww) => r0; move: (pf l le l0 r0) => [ta tb].
  have tc: iclosed_set (fpi l).
     by move: (iclosed_fixpoints_fun oe ta); rewrite /fpi/fixpoints ss.
  have td: ord_cofinal (fpi l) E by rewrite /fpi; apply: normal_fix_cofinal.
  split => //; split => //.
    move => m lm t.
    have lle : l <o E by apply /ord_ltP => //.
    have me: inc m E by apply /ord_ltP => //; ord_tac.
    rewrite /fpi /fixpoints ss // ss //.
    move => /Zo_P [ua ub]; apply /Zo_P; split => //.
    have fv: function (Vf g l) by move: ta => [[]].
    have: inc t (ii l).
       rewrite - tb; apply /(Vf_image_P1 fv); rewrite ss//; exists t => //.
    have ml: inc m l by apply/ord_ltP => //;ord_tac.
    by rewrite /ii => ti; move:(setIf_hi ti ml) => /Zo_P [].
have ph: forall i, inc i E -> i <> \0o ->
  normal_function (Vf g i) E E /\ image_of_fun (Vf g i) = ii i.
  move => i ie inz; apply: pf => //; apply: pe => // k ke; apply: pg.
  have iE: i <o E by apply/ord_ltP.
  apply /ord_ltP => //;ord_tac.
split.
+ move => i ie inz.
  have [ta tb]:iclosed_set (ii i) /\ ord_cofinal (ii i) E.
     apply: pe => // k ke;apply: pg.
     have iE: i <o E by apply/ord_ltP.
     apply /ord_ltP => //; ord_tac.
  move: (proj1 (ordinals_set_iso (proj1 ta))).
  by rewrite (ord_cofinal_p5 pa) -/E // => h t ti; apply: (proj1 tb); apply: h.
+ move => i ide inz; apply: pe => // k ki; apply: pg.
   have iE: i <o E by apply/ord_ltP.
   apply /ord_ltP => //; ord_tac.
+ move => i ide; case (equal_or_not i \0o);first by move => ->; ue.
  move => inz; exact (proj1 (ph i ide inz)).
+ move => i ide inz;exact (proj2 (ph i ide inz)).
Qed.

Lemma many_der_unique C1 C2 f1 f2 (E1:= succ_c C1) (E2:= succ_c C2)
   (g1:= many_der f1 E1) (g2:= many_der f2 E2):
  C1 <=c C2 -> infinite_c C1 ->infinite_c C2 ->
  agrees_on E1 f1 f2 ->
  normal_function f1 E1 E1 -> normal_function f2 E2 E2 ->
  forall i, inc i E1 -> agrees_on E1 (Vf g1 i) (Vf g2 i).
Proof.
move => lcc ic1 ic2 agg n1 n2.
have pa: sub E1 E2.
   move => x /(succ_cP (proj1 ic1)) [sa sb]; apply/(succ_cP (proj1 ic2)).
   split => //; co_tac.
move: (many_der_ex ic1 n1).
move: (many_der_ex ic2 n2).
rewrite -/E1 -/E2 -/g1 -/g2.
set ii1:= fun i => (intersectionf i (fun z => fixpoints (Vf g1 z))).
set ii2:= fun i => (intersectionf i (fun z => fixpoints (Vf g2 z))).
move => [fg2 sg2 g20 g2v [hr2 cc2 ng2 ig2]].
move => [fg1 sg1 g10 g1v [hr1 cc1 ng1 ig1]].
move => i ie1.
have sg1i:source (Vf g1 i) = E1 by move: (ng1 _ ie1) => [[]].
have sg2i:source (Vf g2 i) = E2 by move: (ng2 _ (pa _ ie1)) => [[]].
hnf; rewrite sg1i sg2i; split=> // x xe1; move: x xe1.
have oe1: ordinalp E1 by move:(succ_c_pr1 (proj1 ic1)) => [[]].
have oi: ordinalp i by ord_tac.
pose p i := inc i E1 -> forall x, inc x E1 -> Vf (Vf g1 i) x = Vf (Vf g2 i) x.
apply:(least_ordinal2 (p:=p)) oi ie1 => y oor etc ye1 x xe1 ; clear sg1i sg2i.
case: (equal_or_not y \0o) => ynz.
  by rewrite ynz g10 g20; move: agg => [sa sb sc];rewrite sc.
rewrite g1v // g2v //; last by apply: pa.
move: (cc1 y ye1 ynz) => [sa sc].
move: (cc2 y (pa _ ye1) ynz) => [sb sd].
have se: (ii1 y) = (ii2 y) \cap E1.
  have y0: inc \0o y by apply:ord_ne_has0.
  have yne: nonempty y by exists \0o.
  have lye1: y <o E1 by apply /ord_ltP.
  set_extens t.
    move => ti.
    have te1: inc t E1.
     by move:(setIf_hi ti y0) => /Zo_S; rewrite g10; move: n1 => [[_ -> _ ]].
    apply /setI2_P; split => //.
    apply /setIf_P => // j jy.
    have jy1: j <o y by apply /ord_ltP.
    have je1: inc j E1 by apply /ord_ltP => //; ord_tac.
    have sj1: (source (Vf g1 j)) = E1 by move: (ng1 _ je1) => [[]].
    have sj2: (source (Vf g2 j)) = E2 by move: (ng2 _ (pa _ je1)) => [[]].
    move:(setIf_hi ti jy) => /Zo_P [za zb]; apply /Zo_P; split => //.
      by rewrite sj2; apply: pa.
    by move: (etc _ jy1 je1 t te1) => <-.
  move => /setI2_P [ti2 te1]; apply /setIf_P => // j jy.
  have jy1: j <o y by apply /ord_ltP.
  have je1: inc j E1 by apply /ord_ltP => //; ord_tac.
  have sj1: (source (Vf g1 j)) = E1 by move: (ng1 _ je1) => [[]].
  have sj2: (source (Vf g2 j)) = E2 by move: (ng2 _ (pa _ je1)) => [[]].
  move:(setIf_hi ti2 jy) => /Zo_P [za zb]; apply /Zo_P; split => //.
     by rewrite sj1.
   by move: (etc _ jy1 je1 t te1) ->.
move: (ordinals2_extc ic1 ic2 sa sb sc sd lcc se) => [ra rb rc].
by apply: rc.
Qed.

Definition all_der_bound f b:=
  forall x i, ordinalp x -> ordinalp i ->
    [/\ inc x (b x i), inc i (b x i),
      (exists2 C, infinite_c C & b x i = succ_c C) &
      forall t, inc t (b x i) -> inc (f t) (b x i)].

Lemma all_der_bound_prop f b x i
   (E:= b x i) (C := (\csup (Zo E cardinalp))):
   ordinalp x -> ordinalp i -> all_der_bound f b ->
   [/\ infinite_c C, inc x E, inc i E, E = succ_c C &
    forall t, inc t E -> inc (f t) E].
Proof.
move => ox oi h.
move: (h _ _ ox oi) => [sa sb sc sd].
by move: (succ_c_pred_more sc) => [sz sf].
Qed.

Definition all_der_aux f E x i :=
   Vf (Vf (many_der (Lf f E E) E) i) x.

Definition all_der f b x i := all_der_aux f (b x i) x i.

Section All_derivatives.
Variable f: fterm.
Variable b: fterm2.
Hypothesis nf: normal_ofs f.
Hypothesis bf: all_der_bound f b.
Let g := all_der f b.

Lemma all_der_bound_prop2 x i:
   ordinalp x -> ordinalp i ->
   normal_function (Lf f (b x i) (b x i)) (b x i) (b x i).
Proof.
move => sa sb; move: (all_der_bound_prop sa sb bf).
move => [ta tb tc td te].
rewrite td in te.
by move:(normal_ofs_restriction ta nf te); rewrite - td.
Qed.

Lemma all_der_p1 x i C (E:= succ_c C):
  infinite_c C ->
  inc x E -> inc i E -> (forall t, inc t E -> inc (f t) E) ->
  g x i = all_der_aux f E x i.
Proof.
move => pc xe ie esf.
move: (proj1 (CS_succ_c (proj1 pc))) => oe.
have ox: ordinalp x by ord_tac.
have oi: ordinalp i by ord_tac.
move:(all_der_bound_prop2 ox oi) => n1.
move:(normal_ofs_restriction pc nf esf) => n2.
move:(all_der_bound_prop ox oi bf).
have aux: forall C C', C <=c C' -> sub (succ_c C) (succ_c C').
   move => C1 C2 cc; move: (cc) => [c1 c2 _].
   move => t /(succ_cP c1) [sa sb]; apply/(succ_cP c2).
   split => //; co_tac.
set C1 := union _; move => [sa sb sc sd se].
rewrite {3 4} sd in n1.
case: (card_le_to_ee (proj1 pc) (proj1 sa)) => cc.
  have see: sub E (b x i) by rewrite sd;apply: aux.
  have ag: agrees_on E (Lf f E E) (Lf f (b x i) (b x i)).
    by hnf; aw; split => // => t te /=; aw; apply: see.
  move: (many_der_unique cc pc sa ag n2 n1 ie) => [_ _ h].
  by symmetry; move: (h _ xe); rewrite - sd.
have see: sub (b x i) E by rewrite sd;apply: aux.
have ag: agrees_on (b x i) (Lf f (b x i) (b x i)) (Lf f E E).
    by hnf; aw; split => // => t te /=; aw; apply: see.
rewrite {1} sd in ag; rewrite sd in sb sc.
move: (many_der_unique cc sa pc ag n1 n2 sc)=> [_ _ h].
by move: (h _ sb); rewrite - sd.
Qed.

Lemma all_der_p2 x: ordinalp x -> g x \0o = f x.
Proof.
move => ox.
move: (all_der_bound_prop ox OS0 bf).
move: (all_der_bound_prop2 ox OS0).
move => sa [sb sc sd se sf].
rewrite se in sa.
move:(many_der_ex sb sa).
move => [pa pb pc _ _]; move: (f_equal (Vf^~ x) pc); rewrite - se;aw.
Qed.

Lemma all_der_p3 x i: ordinalp x -> ordinalp i -> inc (g x i) (b x i).
Proof.
move => ox oi.
move: (all_der_bound_prop ox oi bf).
move: (all_der_bound_prop2 ox oi).
move => sa [sb sc sd se sf]; rewrite se in sa.
move:(many_der_ex sb sa).
move => [_ _ _ _ [_ _ pg _]].
rewrite se in sd.
move: (pg _ sd); rewrite - se; move => [[p1 p2 p3] _ _].
rewrite -p3 /g/all_der/all_der_aux; Wtac.
Qed.

Lemma OS_all_der x i: ordinalp x -> ordinalp i -> ordinalp (g x i).
Proof.
move=> ox oi; move: (all_der_p3 ox oi) => h.
move: (all_der_bound_prop ox oi bf) => [sb _ _ se _].
move: (proj1 (CS_succ_c (proj1 sb))) => ose.
rewrite se in h; ord_tac.
Qed.

Lemma all_der_p4 x y i: ordinalp i -> x <o y ->
  [/\ inc x (succ_c (union (Zo (b y i) cardinalp))),
       g x i = all_der_aux f (b y i) x i &
      g y i = all_der_aux f (b y i) y i].
Proof.
move => oi xy.
move: (xy) => [[ox oy _] _].
move: (all_der_bound_prop oy oi bf).
move => [sa sb sc sd se].
rewrite sd in sb sc se.
have xe: inc x (succ_c (union (Zo (b y i) cardinalp))).
  move: (proj1 (CS_succ_c (proj1 sa))) => ose.
  move/(ord_ltP ose): sb => lt1.
  apply/(ord_ltP ose); ord_tac.
rewrite (all_der_p1 sa xe sc se); split => //.
rewrite /g /all_der - sd//.
Qed.

Lemma all_der_p5 x y i: ordinalp i -> x <o y -> g x i <o g y i.
Proof.
move => oi xy.
move: (all_der_p4 oi xy) => [xe v1 v2].
move: (xy) => [[ox oy _] _].
move: (all_der_bound_prop oy oi bf) => [sa sb sc sd se].
move: (all_der_bound_prop2 oy oi); rewrite sd => sf.
rewrite sd in sb sc se.
move:(many_der_ex sa sf) => [_ _ _ _ [_ _ ug _]].
move: (ug _ sc) => [ua ub _];move: (ub _ _ xe sb xy).
rewrite - sd v1 v2//.
Qed.

Lemma all_der_p5' x y i: ordinalp i -> ordinalp x -> ordinalp y ->
  g x i = g y i -> x = y.
Proof.
move => oi ox oy h.
by case: (ord_le_to_ell ox oy) => // /(all_der_p5 oi) []; rewrite h.
Qed.

Lemma all_der_p5'' x y i: ordinalp i -> ordinalp x -> ordinalp y ->
  g x i <o g y i -> x<o y.
Proof.
move => oi ox oy h.
case: (ord_le_to_ell oy ox) => //.
  by move => eq; move: (proj2 h); rewrite eq.
move/(all_der_p5 oi) => [h1 _]; ord_tac.
Qed.

Lemma all_der_p6 i: ordinalp i -> normal_ofs (g ^~i).
Proof.
move => oi.
split; first by move => x y xy;apply:all_der_p5.
move => x lx.
move: (lx) => [ox _ _].
have osx: forall y, inc y x -> y <o x by move => y yx; ord_tac.
pose g1 y := all_der_aux f (b x i) y i.
have -> : g x i = g1 x by [].
have -> : fun_image x (g^~ i) = fun_image x g1.
  have h: forall z, inc z x -> g z i = g1 z.
     move => z zx.
     have zx': z <o x by ord_tac.
     by move: (all_der_p4 oi zx') => [_ -> _].
  by set_extens t; move =>/funI_P [z zx ->]; apply/funI_P; ex_tac; rewrite h.
move: (all_der_bound_prop ox oi bf) => [sa sb sc sd se].
move: (all_der_bound_prop2 ox oi); rewrite sd => sf.
move:(many_der_ex sa sf) => [_ _ _ _ [_ _ ug _]].
rewrite - sd in ug.
move: (ug _ sc); set g2 := (Vf _ _).
have <-: Vf g2 x = g1 x by [].
move => [[ fg2 sg2 _] hb hc]; rewrite (hc _ sb lx); congr union.
have sgx: sub x (source g2).
   rewrite sg2;apply:ordinal_transitive => //; rewrite sd.
   exact: (proj1 (CS_succ_c (proj1 sa))).
set_extens t.
  move/(Vf_image_P fg2 sgx) => [u ut ->]; apply/funI_P; ex_tac.
move /funI_P=> [u ut ->]; apply /(Vf_image_P fg2 sgx); ex_tac.
Qed.

Lemma all_der_p7 x i j: ordinalp x -> i <o j -> g (g x j) i = g x j.
Proof.
move => ox ij.
move: (ij) => [[oi oj _] _].
move: (all_der_bound_prop ox oj bf).
set C:= union _; set E:= succ_c C.
move => [sa sb sc sd se].
rewrite sd in sb sc se.
have ie: inc i E.
  move: (proj1 (CS_succ_c (proj1 sa))) => ose.
  move/(ord_ltP ose): sc => lt1.
  apply/(ord_ltP ose); ord_tac.
set ge:= all_der_aux f E.
set y := (g x j).
have ye: inc y E by rewrite - sd; apply:all_der_p3.
have v1: g y i = ge y i by exact: (all_der_p1 sa ye ie se).
have v2: y = ge x j by exact (all_der_p1 sa sb sc se).
have jnz: j <> \0o by move => h; move: ij; rewrite h; exact:ord_lt0.
move: (all_der_bound_prop2 ox oj); rewrite sd => sf.
move:(many_der_ex sa sf); rewrite -/E.
set ff:=(many_der (Lf f E E) E).
move => [ua ub uc ud [_ _ ug uh]].
move: (ud _ sc jnz) => h.
have v3: y = Vf (Vf ff j) x by ue.
move: (ug _ sc) => [[pa pb pc] _ _].
have ij': inc i j by apply/ord_ltP => //.
have: inc y (image_of_fun (Vf ff j)).
    rewrite v3; apply/(Vf_image_P1 pa); exists x => //; ue.
rewrite (uh j sc jnz) v1 => h1.
by move: (setIf_hi h1 ij') => /Zo_hi.
Qed.

Lemma all_der_p8 y j: ordinalp y -> \0o <o j ->
   (forall i, i <o j -> g y i = y) ->
   (exists2 x, ordinalp x & y = g x j).
Proof.
move => oy jp h.
have oj: (ordinalp j) by ord_tac.
have jnz: j <> \0o by move => bad; move:jp => []; rewrite bad.
move: (all_der_bound_prop oy oj bf).
set C:= union _; set E:= succ_c C.
move => [sa sb sc sd se].
move: (all_der_bound_prop2 oy oj); rewrite sd => sf.
rewrite sd in sb sc se.
move:(many_der_ex sa sf); rewrite -/E.
set ff:=(many_der (Lf f E E) E).
move => [ua ub uc ud [_ _ ug uh]].
move: (proj1 (CS_succ_c (proj1 sa))) => oe.
suff:inc y (image_of_fun (Vf ff j)).
  move: (ug _ sc) => [[pa pb pc] _ _ ].
  move /(Vf_image_P1 pa); rewrite pb; move => [u ue uv]; exists u.
     ord_tac.
  by rewrite uv (all_der_p1 sa ue sc).
rewrite (uh j sc jnz).
have nej: nonempty j by exists \0o;ord_tac.
apply /setIf_P => // i ij.
have ij1: i <o j by ord_tac.
have iE: inc i E by move/(ord_ltP oe): sc => h1; apply/(ord_ltP oe); ord_tac.
move: (ug _ iE) => [[pa pb pc] _ _ ].
apply /Zo_P; rewrite pb; split => //.
by move:(h _ ij1);rewrite (all_der_p1 sa sb iE se).
Qed.

Lemma all_der_p9 x y i j:
  ordinalp x -> ordinalp y -> ordinalp i -> ordinalp j ->
  (g x i = g y j <->
   [/\ i <o j -> x = g y j, i = j -> x = y & j <o i -> y = g x i]).
Proof.
move => ox oy oi oj.
case (ord_le_to_ell oi oj).
+ move => ->.
  split; last by move => [_ ok _]; rewrite (ok (erefl j)).
  move /(all_der_p5' oj ox oy) => xy.
  by split => // [] [].
+ move => lij.
  move: (all_der_p7 oy lij) => eq1.
  split.
    rewrite - {1} eq1; move /(all_der_p5' oi ox (OS_all_der oy oj)) => <-.
    split => //.
    - by move => ii; move: (proj2 lij); rewrite ii.
    - by move => [ii _] ; ord_tac.
  by move => [h _ _]; rewrite (h lij).
+ move => lji.
  move: (all_der_p7 ox lji) => eq1.
  split.
    rewrite - {1} eq1 => eq; symmetry in eq.
    rewrite - (all_der_p5' oj oy (OS_all_der ox oi) eq).
    split => //.
    - by move => [ii _] ; ord_tac.
    - by move => ii; move: (proj2 lji); rewrite ii.
  by move => [_ _ h]; rewrite (h lji).
Qed.

Lemma all_der_p10 x y i j:
  ordinalp x -> ordinalp y -> ordinalp i -> ordinalp j ->
  (g x i <o g y j <->
   [/\ i <o j -> x <o g y j, i = j -> x <o y & j <o i -> g x i <o y]).
Proof.
move => ox oy oi oj.
case (ord_le_to_ell oi oj).
+ move => ->; split.
  by move /(all_der_p5'' oj ox oy) => h; split => // [][].
  move => [_ ok _]; exact: (all_der_p5 oj (ok (erefl j))).
+ move => lij.
  move: (all_der_p7 oy lij) => eq1.
  split.
    rewrite - {1} eq1; move /(all_der_p5'' oi ox (OS_all_der oy oj)) => h1.
    split => //.
    - by move => ii; move: (proj2 lij); rewrite ii.
    - by move => [ii _] ; ord_tac.
    - by move => [h _ _]; rewrite - eq1; apply:all_der_p5 => //; apply:h.
+ move => lji.
  move: (all_der_p7 ox lji) => eq1.
  split.
    rewrite - {1} eq1; move /(all_der_p5'' oj (OS_all_der ox oi) oy) => h1.
    split => //.
    - by move => [ii _] ; ord_tac.
    - by move => ii; move: (proj2 lji); rewrite ii.
    - by move => [_ _ h]; rewrite - eq1; apply:all_der_p5 => //; apply:h.
Qed.

Lemma all_der_p10' x y i j:
  ordinalp x -> ordinalp y -> ordinalp i -> ordinalp j ->
  (g x i <=o g y j <->
   [/\ i <o j -> x <=o g y j, i = j -> x <=o y & j <o i -> g x i <=o y]).
Proof.
move => ox oy oi oj.
move: (all_der_p9 ox oy oi oj) (all_der_p10 ox oy oi oj) => ha hb.
case: (equal_or_not (g x i) (g y j)) => h2.
  move/ha:(h2) => [sa sb sc].
  split => // _.
    split => // h3.
    + rewrite - (sa h3); ord_tac.
    + rewrite - (sb h3); ord_tac.
    + rewrite - (sc h3); ord_tac.
  by rewrite h2; apply: ord_leR; apply:OS_all_der.
split => h.
  move/hb: (conj h h2) => [sa sb sc].
    split => // h3.
    + exact: (proj1 (sa h3)).
    + exact: (proj1 (sb h3)).
    + exact: (proj1 (sc h3)).
have[//]: g x i <o g y j.
move:h => [sa sb sc].
apply/ hb; split => // h3; split; fprops => ne; case h2; apply /ha.
+ split => //.
    by move => ii; move: (proj2 h3); rewrite ii.
  by move => [ii _] ; ord_tac.
+ by rewrite h3; split => // [] [].
+ split => //.
  by move => [ii _] ; ord_tac.
  by move => ii; move: (proj2 h3); rewrite ii.
Qed.

Lemma all_der_p11 x i j : ordinalp x -> i <=o j -> g x i <=o g x j.
Proof.
move => ox ij.
move: (ij) => [oi oj _].
apply/(all_der_p10' ox ox oi oj); split => //.
+ move => lij.
  exact: (osi_gex (proj1 (all_der_p6 oj)) ox).
+ move => _; ord_tac.
+ move => h; ord_tac.
Qed.

Lemma all_der_p12 i : ordinalp i -> f \0o = \0o -> g \0o i = \0o.
Proof.
move => oi f0.
apply: (least_ordinal2 (p:= fun z => g \0o z = \0o)) oi => y oy etc.
case: (ord_zero_dichot oy) => yz; first by rewrite yz all_der_p2 //; fprops.
move: (all_der_p8 OS0 yz etc)=> [x ox h].
case (ord_zero_dichot ox) => xz; first by rewrite - {1} xz.
by move: (all_der_p5 oy xz); rewrite - h; move /ord_lt0.
Qed.

Lemma all_der_p13 : f \0o <> \0o -> normal_ofs (g \0o).
Proof.
move => fz.
have pa: forall i j, i <oj -> g \0o i <o g \0o j.
  move => i j ij;move:(ij)=> [[oi oj _] _].
  apply/(all_der_p10 OS0 OS0 oi oj); split => //.
  + move => _; apply: ord_ne0_pos; first by apply:(OS_all_der OS0 oj).
    move => eq1; move:(all_der_p7 OS0 (ord_le_ltT (ozero_least oi) ij)).
    by rewrite eq1 (all_der_p2 OS0).
  + by move: (proj2 ij).
  + move => [ij1 _]; ord_tac.
split => // j lj.
move: lj => [oj z0j lj1].
have osi: ordinal_set (fun_image j (g \0o)).
  move => t /funI_P [i ij ->]; apply:OS_all_der; fprops; ord_tac.
have oy: (ordinalp (g \0o j)) by apply:OS_all_der; fprops.
have ubb: ordinal_ub (fun_image j (g \0o)) (g \0o j).
  move => t /funI_P [i ij ->].
  have ij1: i <o j by ord_tac.
  exact (proj1 (pa _ _ ij1)).
set ww := \osup (fun_image j (g \0o)).
pose Tk k := (fun_image (Zo j (fun z => k <o z)) (g \0o)).
have wa: forall k, k <o j -> inc (g \0o (succ_o k)) (Tk k).
  move => k kj.
  have ikj: inc k j by apply/ord_ltP.
  move:(lj1 _ ikj) => kj1.
  apply/funI_P; exists (succ_o k) => //; apply/Zo_P; split => //.
  apply: ord_succ_lt; ord_tac.
have pc: forall k, k <o j -> \osup (Tk k) = ww.
  move => k kj.
  apply: ord_sup_1cofinal => //; split.
    move => t /funI_P [z /Zo_S zj ->]; apply/funI_P; ex_tac.
  move => x /funI_P [z zj ->].
  have oz: ordinalp z by ord_tac.
  have ok: ordinalp k by ord_tac.
  case (ord_le_to_el oz ok) => kz.
    exists (g \0o (succ_o k)); first by exact:(wa _ kj).
    by move /ord_lt_succP: kz => /pa [].
  exists (g \0o z); first by apply/funI_P; exists z => //;apply/Zo_P.
  apply: ord_leR; apply:OS_all_der; fprops.
move: (all_der_bound_prop OS0 oj bf).
set C:= union _; set E:= succ_c C.
move => [sa sb sc sd se].
rewrite sd in sb sc se.
move: (all_der_bound_prop2 OS0 oj); rewrite sd => sf.
move:(many_der_ex sa sf); rewrite -/E.
set ff:= (many_der (Lf f E E) E).
pose ii i:= (intersectionf i (fun z : Set => fixpoints (Vf ff z))).
move=> [ua ub uc _ [_ _ ug uh]].
have oe:= proj1(CS_succ_c (proj1 sa)).
have lje: j <o E by ord_tac.
have wwe: inc ww E.
  have p1:sub (fun_image j (g \0o)) E.
     move => t /funI_P [z za ->].
     have ze:=(ordinal_transitive oe sc za).
     move: (ug _ ze) => [[fg sg tg] _ _].
     rewrite (all_der_p1 sa sb ze se).
     rewrite -tg -/E /all_der_aux -/ff; Wtac.
   apply: succ_c_sup => //.
   move/(succ_cP (proj1 sa)):sc => [_ rd].
   exact: (card_leT (fun_image_smaller j (g \0o)) rd).
have pd: forall k, k<o j -> ww = g ww k.
  move => k kj; rewrite - (pc _ kj).
  have pr1: forall x, inc x (Tk k) -> x = g x k.
    move => x /funI_P [i /Zo_P [va vb] eq].
    by move: (all_der_p7 OS0 vb); rewrite - eq.
  set Z:= fun_image (Tk k) (g^~k).
  have h1: Tk k = Z.
       set_extens t.
          move => tk; move:(pr1 t tk) => eq1; apply/funI_P; ex_tac.
       by move => /funI_P [z za zb]; move: (pr1 _ za); rewrite - zb => <-.
  have nez: nonempty Z by rewrite - h1; move: (wa _ kj) => h; ex_tac.
  have h2: forall x, inc x Z -> \0o <=o x.
    rewrite -h1; move => x /funI_P [i /Zo_P[ta tb] ->].
    move: (OS_all_der OS0 (proj32_1 tb)) => h; ord_tac1.
  move: (all_der_p6 (proj31_1 kj)) => na.
  move/normal_ofs_equiv1: na => [_ h3].
  by move: (h3 _ h2 nez); rewrite - h1 -/Z - h1.
have pe: inc ww (image_of_fun (Vf ff j)).
   have nej: nonempty j by ex_tac.
   case:(equal_or_not j \0o) => jnz; first by move: z0j; rewrite jnz;case;case.
   rewrite (uh _ sc jnz); apply/setIf_P => // k kj.
   have lkj: k <o j by ord_tac.
   have ke: inc k E by move: (ord_lt_leT lkj (proj1 lje)) =>h; ord_tac.
   move: (ug _ ke) => [[va vb vc] _ _ ].
   have eq1: Vf (Vf ff k) ww = ww.
     by symmetry;rewrite {1} (pd _ lkj) (all_der_p1 sa wwe ke se).
   by apply/Zo_P; rewrite vb.
move: (ug _ sc) => [[va vb vc] _ _ ].
move /(Vf_image_P1 va): pe; rewrite vb; move => [u ue eq2].
have eq3: ww = g u j by rewrite (all_der_p1 sa ue sc se) eq2.
move: (ord_ub_sup osi oy ubb);rewrite -/ww eq3 => h.
case: (equal_or_not u \0o) => uz; first by rewrite uz.
have luz : \0o <o u. apply:ord_ne0_pos => //; ord_tac.
move: (all_der_p5 oj luz) => h'; ord_tac.
Qed.

Lemma all_der_p14 i: ordinalp i ->
  first_derivation (g ^~i) =1o g ^~(succ_o i).
Proof.
move=> oi.
have sa:= (first_derivation_p0 (all_der_p6 oi)).
have sb:= (all_der_p6 (OS_succ oi)).
move: (proj1 sa)(proj1 sb) => ia ib.
move: (first_derivation_p (all_der_p6 oi))=> [sc sd].
apply:sincr_ofs_exten => // x ox.
  move:(sc _ ox); set y := first_derivation (g^~ i) x => h.
  have snz:= (ord_lt0_succ oi).
  have oxx:=(ofs_sincr ia ox).
  apply: all_der_p8 => // j jsi.
  case (equal_or_not j i) => eji; first by rewrite eji.
  have ji: j <o i by move /ord_lt_succP: jsi => h1.
  by move: (all_der_p7 oxx ji); rewrite -/y h.
have oy:=(OS_all_der ox (OS_succ oi)).
have se:= (all_der_p7 ox (ord_succ_lt oi)).
exact: (sd _ oy se).
Qed.

End All_derivatives.

Lemma all_der_p12_bis f b (g:= all_der f b) z:
  normal_ofs f ->all_der_bound f b ->
  ordinalp z -> (forall x, x <o z -> f x = x) ->
  (forall x i, x <o z -> ordinalp i -> g x i = x).
Proof.
move => ha hb oz fp x i xi oi0;move:x xi.
rename i into i0.
pose p i :=forall x, x <o z -> g x i = x.
apply: (least_ordinal2 (p :=p)) oi0 => i oi etc x xz.
have ox: ordinalp x by ord_tac.
case (ord_zero_dichot oi) => iz.
  by rewrite iz /g (all_der_p2 ha hb ox) (fp _ xz).
have hc: forall x, x <o z -> exists2 t, t <=o x & x = g t i.
  move => u uz.
  have ou: ordinalp u by ord_tac.
  have hh: forall k, k<o i -> g u k = u by move => k ki; move: (etc _ ki _ uz).
  move:(all_der_p8 ha hb ou iz hh) => [t ot]; rewrite -/g => eq1.
  move: (proj1 (all_der_p6 ha hb oi)) => hc.
  by move:(osi_gex hc ot); rewrite -/g - eq1 => tu; exists t.
apply: (least_ordinal2(p:= fun x => x<o z -> g x i = x)) ox (xz) => x0 ox0 etc2.
move => le2.
move: (hc _ le2) => [t ta eq1].
case (equal_or_not t x0) => eq2; first by rewrite - eq2 - eq1.
by move: (etc2 _ (conj ta eq2) (ord_le_ltT ta le2)) => eq3;rewrite eq1 eq3.
Qed.

Lemma all_der_p13_bis f b (g:= all_der f b) z:
  normal_ofs f ->all_der_bound f b ->
  ordinalp z -> (forall x, x <o z -> f x = x) ->
  f z <> z -> normal_ofs (g z).
Proof.
move => ha hb oz hc fzz.
move: (all_der_p12_bis ha hb oz hc) => hd.
set fs := (fun t => f (z +o t) -o z).
move: (normal_shift ha oz);rewrite -/fs => ha'.
pose bs x i := b (z +o x) i.
have hb': all_der_bound fs bs.
  move => x i ox oi; move:(hb _ _ (OS_sum2 oz ox) oi);rewrite /bs.
  move => [pa pb pc pd]; move: (pc) => [C ic eq].
  have oE := (proj1 (CS_succ_c (proj1 ic))).
  rewrite eq in pa;move/(ord_ltP oE): pa => pa'.
  have zE: inc z (b (z +o x) i).
    move: (osum_Mle0 oz ox)=> pa''; rewrite eq; apply/(ord_ltP oE); ord_tac.
  have pe: inc x (b (z +o x) i).
   move: (osum_M0le oz ox)=> pa''; rewrite eq; apply/(ord_ltP oE); ord_tac.
  split => // t tE.
  have: inc (z +o t) (b (z+o x) i).
    move: zE tE; move: (succ_c_sum ic); rewrite -eq; apply.
  move/pd; rewrite eq; move/(ord_ltP oE) => za; apply /(ord_ltP oE).
  move: (odiff_Mle oz (proj31_1 za)) => eq2; ord_tac.
have zfz: z <o f z.
  split; fprops; exact: (osi_gex (proj1 ha) oz).
have fsz: fs \0o <> \0o.
  by rewrite /fs (osum0r oz) => h; move: (odiff_pos zfz) => []; rewrite h.
move: (all_der_p13 ha' hb' fsz) => he.
set gs:= (all_der fs bs).
have ns:= (osum_normal oz).
suff: forall i, ordinalp i -> forall x, ordinalp x ->
  z +o (gs x i) = g (z +o x) i.
  move => h.
  have n1: forall i, ordinalp i -> g z i = z +o (gs \0o i).
    by move => i oi; move: (h i oi \0o OS0); rewrite (osum0r oz).
  move: (normal_ofs_compose ns he).
  by apply:normal_ofs_from_exten => t ot /=; rewrite - n1.
move => i0 oi0.
pose p i := forall x, ordinalp x -> z +o gs x i = g (z +o x) i.
apply: (least_ordinal2 (p:=p)) oi0 => i oi etc.
have pa: forall f x, normal_ofs f -> ordinalp x ->
    (ordinalp (f (z +o x) -o z) /\ z +o (f (z +o x) -o z) = f (z +o x)).
  move => h x [sih _] ox; move:(osi_gex sih (OS_sum2 oz ox))=> xh.
  by move: (odiff_pr (ord_leT (osum_Mle0 oz ox) xh)) => [].
move => x ox.
have na: normal_ofs (fun x => z +o gs x i).
  exact: (normal_ofs_compose ns (all_der_p6 ha' hb' oi)).
have nb: normal_ofs (fun x => g (z +o x) i).
  exact: (normal_ofs_compose (all_der_p6 ha hb oi) ns).
case (ord_zero_dichot oi) => iz.
   have ozx: ordinalp (z +o x) by fprops.
   rewrite iz /g /gs (all_der_p2 ha hb ozx) (all_der_p2 ha' hb' ox).
   apply: (proj2 (pa _ _ ha ox)).
have ee: forall y j, j <o i -> ordinalp y -> z +o gs y j = g (z +o y) j.
  by move => y j ji oy; move:(etc _ ji _ oy).
move: x ox.
apply:(sincr_ofs_exten (proj1 na) (proj1 nb)) => x ox.
  move: (ofs_sincr (proj1 na) ox) => o1.
  have ot: (ordinalp (gs x i)) by apply: (OS_all_der ha' hb' ox oi).
  have h: forall j, j <o i -> g (z +o gs x i) j = z +o gs x i.
    move => j ji.
    by rewrite - (ee _ _ ji ot) /gs (all_der_p7 ha' hb' ox ji).
  move: (all_der_p8 ha hb o1 iz h) => [y y1 y2]; rewrite y2 -/g.
  case: (ord_le_to_el oz y1) => eq1.
    by move: (odiff_pr eq1) => [sa sb]; exists (y -o z) => //; rewrite - sb.
  move: y2 (osum_Mle0 oz ot);rewrite (hd _ _ eq1 oi) => -> h1; ord_tac.
set u :=(g (z +o x) i -o z).
move: (pa _ x (all_der_p6 ha hb oi) ox) => []; rewrite -/g -/u => [ou uv].
have h: forall j, j <o i -> all_der fs bs u j = u.
  move => j ji.
  have ogu:= (OS_all_der ha' hb' ou (proj31_1 ji)).
  apply: (osum2_simpl ogu ou oz); rewrite ee // uv.
  exact:(all_der_p7 ha hb (OS_sum2 oz ox) ji).
rewrite - uv.
by move: (all_der_p8 ha' hb' ou iz h) => [y oy ->]; exists y.
Qed.

Lemma all_der_p13_ter f b (g:= all_der f b):
  normal_ofs f -> all_der_bound f b ->
  f \0o = \0o -> f \1o <> \1o -> normal_ofs (g \1o).
Proof.
move => ha hb hc hd; apply:(all_der_p13_bis) => //; fprops.
by move => x /ord_lt1 ->.
Qed.

Lemma all_der_unique f1 f2 b1 b2
   (g1:= all_der f1 b1) (g2:= all_der f2 b2):
   normal_ofs f1 -> all_der_bound f1 b1 -> all_der_bound f2 b2 ->
   (f1 =1o f2) ->
   (forall x i, ordinalp x -> ordinalp i -> g1 x i = g2 x i).
Proof.
move => nf1 h1 h2 sv.
have nf2 := (normal_ofs_from_exten sv nf1).
move => x i ox oi; move: x ox.
pose p i := (forall x, ordinalp x -> g1 x i = g2 x i).
apply: (least_ordinal2 (p:=p)) oi => y oy etc.
case: (ord_zero_dichot oy) => yp.
  rewrite /p yp => x ox.
  by rewrite /g1 /g2 (all_der_p2 nf1 h1 ox)(all_der_p2 nf2 h2 ox) sv.
move: (all_der_p6 nf1 h1 oy); rewrite -/g1; move=> [pa _].
move: (all_der_p6 nf2 h2 oy); rewrite -/g2; move=> [pb _].
apply:(sincr_ofs_exten pa pb) => x ox.
  move:(ofs_sincr pa ox) => sa.
  apply:(all_der_p8 nf2 h2 sa yp).
  move => j jy; rewrite -/g2 - (etc j jy _ sa).
  exact :(all_der_p7 nf1 h1 ox jy).
move:(ofs_sincr pb ox) => sa.
apply:(all_der_p8 nf1 h1 sa yp).
move => j jy;rewrite -/g1 (etc j jy _ sa).
exact :(all_der_p7 nf2 h2 ox jy).
Qed.

End Ordinals2.
Export Ordinals2.