Library sset13

Theory of Sets : Ordinals

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


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

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

Module Ordinals3.

Cantor normal form

Any ordinal is uniquely a sum of decreasing powers of omega

Definition CNF_graph f :=
 fgraph f /\ (exists2 n, inc n Bnat & domain f = Bint n).

Lemma CNF_domain X: CNF_graph X ->
   domain X = Bint (cardinal (domain X)).
Proof. move => [_ [m mB mv]]; rewrite mv; bw. Qed.

Lemma CNF_domain1 X: CNF_graph X -> inc (cardinal (domain X)) Bnat.
Proof. move => [_ [m mB ->]]; bw. Qed.

Lemma CNF_domain2 X i: CNF_graph X -> inc i (domain X) -> inc i Bnat.
Proof. by move => [_ [m mB ->]] /Bint_S1. Qed.

Lemma CNF_domainP X: CNF_graph X ->
  forall i, (inc i (domain X) <-> i <c (cardinal (domain X))).
Proof.
move=> h; rewrite {1} (CNF_domain h)=> i; bw => //.
have aux: inc (cardinal (domain X)) Bnat by apply: CNF_domain1.
exact: (BintP aux).
Qed.

Definition CNFrv0 f n := osum_expansion (fun i => (omega0 ^o (f i))) n.
Definition CNFrv f := CNFrv0 (Vg f) (cardinal (domain f)).
Definition CNFr_ax0 f n:=
  ord_below_n f n
  /\ (forall i, inc i Bnat -> (succ i) <c n -> (f i) <=o (f (succ i))).

Definition CNFr_ax f:=
  [/\ CNF_graph f,
  (allf f ordinalp) &
  (forall i, inc i (domain f) -> inc (succ i) (domain f) ->
     (Vg f i) <=o (Vg f (succ i)))].

Lemma CNFr_ax_p1 f n: inc n Bnat -> CNFr_ax0 f n ->
  CNFr_ax (Lg (Bint n) f).
Proof.
move => nB [ax1 ax2].
split; first by split; [ fprops | exists n; bw].
  by hnf; bw => x xd; bw; apply: ax1; apply /(BintP nB).
bw; move => i i1 i2; bw.
apply: ax2; [exact: (Bint_S1 i1) | by apply /(BintP nB)].
Qed.

Lemma CNFr_ax_p2 f: CNFr_ax f -> CNFr_ax0 (Vg f) (cardinal(domain f)).
Proof.
move => [ax1 ax2 ax3].
split; first by move => i /(CNF_domainP ax1) idf; apply: ax2.
move => i ib si.
move: (card_le_ltT (card_le_succ ib) si) => /(CNF_domainP ax1) id1.
by apply: ax3 => //; apply /(CNF_domainP ax1).
Qed.

Lemma CNFr_ax_p3 f n: inc n Bnat -> CNFr_ax0 f (succ n) -> CNFr_ax0 f n.
Proof.
move => nB [ax1 ax2]; move: (card_lt_succ nB) => pa;split=> //.
  move=> i lin; apply: ax1; co_tac.
move => i iB si; apply: ax2 => //; co_tac.
Qed.

Lemma CNFr_p1 f n: inc n Bnat ->
  CNFrv (Lg (Bint n) f) = CNFrv0 f n.
Proof.
move => nB; rewrite /CNFrv Lg_domain (card_Bint nB).
by apply: (osum_expansion_exten nB) => i lin; bw; apply/(BintP nB).
Qed.

Lemma OS_CNFr0 f n: inc n Bnat -> CNFr_ax0 f n -> ordinalp (CNFrv0 f n).
Proof.
move => nB [ax1 ax2];apply: (OS_osum_expansion nB) => i lin.
apply: (OS_pow OS_omega (ax1 _ lin)).
Qed.

Lemma CNFr_p2 f n: inc n Bnat -> CNFr_ax0 f (succ n) ->
  (omega0 ^o (f n)) <=o CNFrv0 f (succ n).
Proof.
move => nB ax; move:(CNFr_ax_p3 nB ax) => axr.
rewrite /CNFrv0 (osum_expansion_succ _ nB).
have lt1:= (card_lt_succ nB).
apply: osum_Mle0; last by apply: (OS_CNFr0 nB axr).
by apply: (OS_pow OS_omega (proj1 ax _ (card_lt_succ nB))).
Qed.

Lemma CNFr_p3 f n i:
  inc n Bnat -> CNFr_ax0 f n -> i <c n ->
  omega0 ^o (f i) <=o CNFrv0 f n.
Proof.
move => nB ax lin.
case (equal_or_not n \0c) => nz.
  by move: lin; rewrite nz=> /card_lt0.
move: (cpred_pr nB nz) => [pB pv]. rewrite pv in ax.
move: (CNFr_p2 pB ax); rewrite - pv => e1.
have rec: forall j, \0c <=c j -> j <c cpred n ->
     omega0 ^o f (succ j) <=o CNFrv0 f n -> omega0 ^o f j <=o CNFrv0 f n.
  move => j oj jln le1.
  have pa := (BS_lt_int jln pB).
  have pb: succ j <c succ (cpred n).
    by apply/(card_lt_succ_leP pB); apply /(card_le_succ_ltP _ pa).
  move: (opow_Momega_le (proj2 ax j pa pb)) => l2; ord_tac.
have ip: \0c <=c i by apply: czero_least; co_tac.
have ipn:i <=c cpred n by apply/(card_lt_succ_leP pB); ue.
exact: (cardinal_c_induction4 BS0 pB e1 rec ip ipn).
Qed.

Lemma CNFr_p4 f n m: inc n Bnat -> CNFr_ax0 f (succ n) ->
  f n <o m -> CNFrv0 f (succ n) <o omega0 ^o m.
Proof.
move => nB ax lt1.
suff: CNFrv0 f (succ n) <=o (omega0 ^o (f n) *o (succ n)).
  have sno: succ n <o omega0.
    apply /ord_ltP; [ apply: OS_omega | apply: BS_succ; fprops ].
  move => h; apply: (ord_le_ltT h).
  move: (lt1) => [[cv _ _ ] _].
  move: (omega_pow_pos cv) => op.
  have: omega0 ^o f n *o succ n <o omega0 ^o f n *o omega0.
    by apply: oprod_Meqlt.
  rewrite - (opow_succ OS_omega cv) => h1; apply: (ord_lt_leT h1).
  by apply: opow_Momega_le; apply /ord_succ_ltP.
set p := f n.
have: f n <=o p by rewrite /p; apply: ord_leR; ord_tac.
move: p; clear lt1 m.
move: n nB f ax; apply: cardinal_c_induction.
  move => f ax p hp.
  move:(OS_pow OS_omega (proj32 hp)) (OS_pow OS_omega (proj31 hp)) => ha hb.
  rewrite succ_zero (oprod1r ha) /CNFrv0 osum_expansion1 //.
  by apply:opow_Momega_le.
move => n nB Hrec f ax m le1.
have snB:=(BS_succ nB).
have le2:=(opow_Momega_le le1).
have h := (ord_leT (proj2 ax n nB (card_lt_succ snB)) le1).
have le3:=(Hrec f (CNFr_ax_p3 snB ax) m h).
move:(osum_Mlele le2 le3).
rewrite {1}/CNFrv0 -(osum_expansion_succ (fun i => omega0 ^o f i) snB).
have osn: ordinalp (succ n) by apply: OS_cardinal; fprops.
rewrite (osum_x_xy (OS_pow OS_omega (proj32 le1)) osn).
by rewrite (osum2_2int BS1 snB) csumC - (Bsucc_rw snB).
Qed.

Lemma CNFr_p5 f: CNFrv0 f \0c = \0o.
Proof. by rewrite /CNFrv0 osum_expansion0. Qed.

Lemma CNFr_p6 f n: inc n Bnat -> CNFr_ax0 f n -> n <> \0c ->
    CNFrv0 f n <> \0o.
Proof.
move => nB ax nz rz.
move: (cpred_pr nB nz) => [sa sb].
rewrite sb in ax rz.
move:(CNFr_p2 sa ax). rewrite rz.
move: (omega_pow_pos (proj1 ax _ (card_lt_succ sa))) => l1 l2; ord_tac.
Qed.

Lemma CNFr_unique1 f g n m: CNFr_ax0 f n -> CNFr_ax0 g m ->
   inc n Bnat -> inc m Bnat -> CNFrv0 f n = CNFrv0 g m ->
   (n = m /\ forall i, i <c n -> f i = g i).
Proof.
move => p1 p2 p3 p4; move: n p3 m p1 p2 p4; apply: cardinal_c_induction.
  move => m af ag mB sv; split; last by move => i /card_lt0 h; case h.
  symmetry;ex_middle bad.
  by move: (CNFr_p6 mB ag bad); rewrite - sv CNFr_p5; case.
move => n nB Hrec m af ag mB sv.
case: (equal_or_not m \0c) => mz.
  by move: (CNFr_p6 (BS_succ nB) af (@succ_nz n)); rewrite sv mz CNFr_p5; case.
move: (cpred_pr mB mz); set m':= (cpred m); move => [m'B mv].
rewrite mv in ag sv.
move: (CNFr_ax_p3 nB af) (CNFr_ax_p3 m'B ag) => ax1 ax2.
move: sv (CNFr_p4 nB af (m:= g m')) (CNFr_p4 m'B ag (m:= f n)).
move:(CNFr_p2 nB af) (CNFr_p2 m'B ag).
rewrite /CNFrv0 (osum_expansion_succ _ nB)(osum_expansion_succ _ m'B).
rewrite -/(CNFrv0 _ _) -/(CNFrv0 _ _).
move => le1 le2 eq1 lt1 lt2.
move: (card_lt_succ nB) (card_lt_succ m'B) => ns1 ns2.
case (ord_le_to_ell (proj1 af _ ns1) (proj1 ag _ ns2)).
+ move => eq2.
  rewrite eq2 in eq1.
  move: (osum2_simpl (OS_CNFr0 nB ax1) (OS_CNFr0 m'B ax2) (proj31 le2) eq1).
  move / (Hrec m' ax1 ax2 m'B)=> [ra rb].
  rewrite mv -ra; split; [ reflexivity | move => i /(card_lt_succ_leP nB) li].
  case (equal_or_not i n); first by move => ->; rewrite eq2 ra.
  by move => ni; apply:rb; split.
+ move /lt1; rewrite eq1 => ll; ord_tac.
+ move /lt2; rewrite - eq1 => ll; ord_tac.
Qed.

Lemma CNFr_unique f g:
  CNFr_ax f -> CNFr_ax g -> CNFrv f = CNFrv g -> f = g.
Proof.
move => af ag; rewrite /CNFrv.
move:(af)(ag) => [af1 _ _ ][ag1 _ _].
move: (CNF_domain1 af1) (CNF_domain1 ag1) (CNF_domain af1) (CNF_domain ag1).
move: (CNFr_ax_p2 af) (CNFr_ax_p2 ag).
set n := (cardinal (domain f)); set m := (cardinal (domain g)).
move => ax1 ax2 nB mB d1 d2 sv;move:(CNFr_unique1 ax1 ax2 nB mB sv)=> [sa sb].
move: (af1) (ag1) => [fgf _] [fg _].
apply: fgraph_exten; [done | done | by rewrite d1 d2 sa | move => i].
rewrite d1; move/(BintP nB); apply: sb.
Qed.

Lemma CNFr_exists x: ordinalp x ->
  exists2 f, CNFr_ax f & x = CNFrv f.
Proof.
move => ox.
suff: exists f n, [/\ inc n Bnat, CNFr_ax0 f n & x = CNFrv0 f n].
  move => [f [n [pa pb pc]]].
  move: (CNFr_ax_p1 pa pb) (CNFr_p1 f pa); rewrite - pc => pd pe.
  by exists (Lg (Bint n) f); [ exact | symmetry].
pose p x := exists f n, [/\ inc n Bnat, CNFr_ax0 f n & x = CNFrv0 f n].
apply: (least_ordinal2 (p:=p)) ox => y oy p2.
case: (ord_zero_dichot oy) => yp.
  exists (@id Set), \0c; split; [ fprops | | by rewrite CNFr_p5 yp].
  split => i; [ move /card_lt0; case | move => _ /card_lt0; case].
move: (ord_ext_div_exists ord_le_2omega yp)
   =>[e1 [c1 [rm [oe1 oc1 or [eq1 lt1 lt2 lt3]]]]].
move: (ord_rev_pred lt3); set c2 := (c1 -o \1o); move => [oc2 eql].
have eql2: c1 = c2 +o \1o.
  move: (osum_M0le OS1 oc2);rewrite - eql => h; move: (ord_le_ltT h lt2) => aa.
  have c2B: inc c2 Bnat by rewrite - /ord_ltP //; ord_tac.
  by move: eql; rewrite (osum2_2int BS1 c2B) csumC - (osum2_2int c2B BS1).
set q := (omega0 ^o e1); set rm1 := ((q *o c2) +o rm).
have oq: ordinalp q by ord_tac.
have op2: ordinalp (q *o c2) by fprops.
have orm1: ordinalp rm1 by rewrite /rm1; fprops.
move: OS_omega OS1 => oo o1.
have yv1: y = q +o rm1 by rewrite eq1 eql osum_prodD // oprod1r // /rm1 osumA.
have pv2: q *o c1 = (q *o c2) +o q by rewrite eql2 osum_prodD // oprod1r //.
have yv2: y = (q *o c2) +o (q +o rm) by rewrite eq1 pv2 osumA //.
have rmy: rm1 <o y.
  split; first by rewrite yv1; apply: osum_M0le => //.
  rewrite /rm1 yv2 => eq3.
  move: (osum_Mle0 oq or);rewrite - (osum2_simpl or (OS_sum2 oq or) op2 eq3).
  move => h; ord_tac.
move:(p2 _ rmy) => [f [n [nB p4 p5]]].
have snb:= (BS_succ nB).
pose g z:= (Yo (z = n) e1 (f z)).
have yv3: y = CNFrv0 g (succ n).
  rewrite /CNFrv0 (osum_expansion_succ _ nB) /g yv1; Ytac0; congr (ord_sum2).
  by rewrite p5;apply: (osum_expansion_exten nB) => i [_ lin]; Ytac0.
have ax2:CNFr_ax0 g (succ n).
  split.
    move => i; rewrite /g; Ytac ein => //.
    by move/(card_lt_succ_leP nB) => lin; apply:(proj1 p4).
  move => i iB h; rewrite /g.
  move/(card_lt_succ_leP nB): h => sin.
  Ytac ein; first by move: (card_lt_succ nB); rewrite - {2} ein => l1; co_tac.
  Ytac sein; last by apply:(proj2 p4).
  move: (card_lt_succ iB); rewrite sein => lin.
  case:(ord_le_to_el (proj1 p4 _ lin) oe1)=> // /ord_succ_ltP/opow_Momega_le sb.
  have aux: omega0 ^o (f i) <=o rm1 by rewrite p5;apply:(CNFr_p3 nB p4 lin).
  move: (osum_Meqlt lt1 op2); rewrite - pv2 => lt6.
  move:(oprod_Meqle (proj1 lt2) oq) => le5.
  move: (ord_lt_leT lt6 le5); rewrite /q - (opow_succ oo oe1) -/rm1 => sc.
  move: (ord_le_ltT aux sc) => sa; ord_tac.
by exists g,(succ n).
Qed.

Definition ord_negl a b := a +o b = b.
Notation "x <<o y" := (ord_negl x y) (at level 60).

Lemma ord_negl_lt a b: ordinalp a -> ordinalp b ->
  a <<o b -> ((a = \0o /\ b = \0o) \/ (a <o b)).
Proof.
rewrite /ord_negl;move=> oa ob abz.
case: (equal_or_not b \0o) => bz.
  by move: abz; rewrite bz (osum0r oa); left.
right; split; first by rewrite -abz; apply: osum_Mle0.
dneg ab; rewrite ab in abz; apply: (osum_a_ab ob ob abz).
Qed.

Lemma ord_negl_sum a b c: ordinalp a -> ordinalp b -> ordinalp c ->
  a <<o b -> a <<o (b +o c).
Proof.
by rewrite /ord_negl => oa ob oc h; rewrite osumA // h.
Qed.

Lemma ord_negl_prod a b c: ordinalp a -> ordinalp b ->
   \0o <o c -> a <<o b -> a <<o (b *o c).
Proof.
move=> oa ob cnz lab.
move: (ord_rev_pred cnz) => [oy ->].
rewrite (osum_prodD OS1 oy ob) (oprod1r ob).
by apply: ord_negl_sum => //; fprops.
Qed.

Lemma ord_negl_sum1 a b c:
  ordinalp a -> ordinalp b -> ordinalp c ->
  a <<o c -> b <<o c -> (a +o b) <<o c.
Proof. rewrite /ord_negl => oa ob oc p1 p2; by rewrite - osumA // p2 p1. Qed.

Lemma ord_negl_sum2 a b c:
  ordinalp a -> ordinalp b -> ordinalp c ->
  (a +o b) <<o c -> (a <<o c /\ b <<o c).
Proof.
rewrite /ord_negl => oa ob oc.
rewrite - (osumA oa ob oc) => h.
suff: b +o c = c by move => h1; rewrite -{2} h h1.
move: (osum_M0le ob oc) => sa.
move: (osum_M0le oa (OS_sum2 ob oc)); rewrite h => h2; ord_tac.
Qed.

Lemma ord_negl_sum3 a b c:
  ordinalp c -> a <=o b -> b <<o c -> a <<o c.
Proof.
move => oc h1; move: (h1) => [oa _ _].
by move: (odiff_pr h1) => [od -> /(ord_negl_sum2 oa od oc) []].
Qed.

Lemma ord_neg_sum4 a b: ordinalp a -> ordinalp b ->
  a <<o (a +o b) -> a <<o b.
Proof. move => oa ob; apply: osum2_simpl; fprops. Qed.

Lemma ord_negl_p1 b: b <o omega0 -> b <<o omega0.
Proof.
move : OS_omega => oo bo.
exact: (indecomp_prop1 indecomp_omega bo).
Qed.

Lemma ord_negl_p2 b e:
  b <o omega0 -> \0o <o e ->
  b <<o (omega0 ^o e).
Proof.
move=> bo ne0.
move: OS_omega OS1 => oo o1.
move: (ord_rev_pred ne0) => [oy ->].
have ob: ordinalp b by ord_tac.
have ooy: ordinalp (omega0 ^o (e -o \1o)) by fprops.
rewrite (opow_sum oo o1 oy) (opowx1 oo).
by apply: ord_negl_prod => //;[ apply: omega_pow_pos | by apply: ord_negl_p1].
Qed.

Lemma ord_negl_p3 b e d:
  b <o omega0 -> \0o <o e -> \0o <o d ->
  b <<o ((omega0 ^o e) *o d).
Proof.
move=> bo ne0 nd0.
have ob: ordinalp b by ord_tac.
have oe: ordinalp e by ord_tac.
move: (ord_negl_p2 bo ne0) => aux.
apply: ord_negl_prod => //; fprops.
Qed.

Lemma ord_negl_p4 a b:
  a <o b -> (omega0 ^o a) <<o (omega0 ^o b).
Proof.
move: OS_omega => ox ltab.
have leac: a <=o b by move: ltab => [? _].
move: (odiff_pr leac) => [odi pa].
move: (odiff_pos ltab) => dp.
have ora: ordinalp a by ord_tac.
have oa1: ordinalp (omega0 ^o a) by fprops.
have oa2: ordinalp (omega0 ^o (b -o a)) by fprops.
rewrite pa (opow_sum ox ora odi) /ord_negl osum_x_xy //.
congr (_ *o _); apply: ord_negl_p2 => //; apply: ord_lt_1omega.
Qed.

Lemma ord_negl_p5 e c e' c':
  c <o omega0 -> e <o e' -> \0o <o c' ->
  ((omega0 ^o e) *o c) <<o ((omega0 ^o e') *o c').
Proof.
move=> ltco ltee' cnz /=.
move: OS_omega => ox.
have leee': e <=o e' by move: ltee' => [? _].
move: (odiff_pr leee') => [odi pa].
have orc: ordinalp e by ord_tac.
have ore: ordinalp c by ord_tac.
have oc': ordinalp c' by ord_tac.
set x:= omega0.
have oa1: ordinalp (x ^o e) by fprops.
have oa2: ordinalp (x ^o (e' -o e)) by fprops.
have oa3: ordinalp ((x ^o (e' -o e)) *o c') by fprops.
have ->: ((x ^o e') *o c') = (x ^o e) *o ( (x ^o (e' -o e)) *o c').
  rewrite oprodA // - (opow_sum ox orc odi) - pa //.
rewrite /ord_negl - osum_prodD //.
by congr (_ *o _); apply: ord_negl_p3 => //; apply: odiff_pos.
Qed.

Lemma ord_negl_p6 e f n:
  inc n Bnat -> CNFr_ax0 f (succ n) ->
  e <o (f n) -> (omega0 ^o e) <<o (CNFrv0 f (succ n)).
Proof.
move: OS_omega => oo nB h ef.
have oe: ordinalp e by ord_tac.
have o1: ordinalp (omega0 ^o e) by fprops.
have o2: ordinalp (omega0 ^o (f n)) by apply:OS_pow => //; ord_tac.
have lt1:= (ord_negl_p4 ef).
rewrite /CNFrv0 (osum_expansion_succ _ nB); apply: ord_negl_sum => //.
exact:(OS_CNFr0 nB (CNFr_ax_p3 nB h)).
Qed.

Lemma ord_negl_pg f n g m:
  inc n Bnat -> CNFr_ax0 f (succ n) ->
  inc m Bnat -> CNFr_ax0 g (succ m) ->
  (g m <o f n <-> CNFrv0 g (succ m) <<o CNFrv0 f (succ n)).
Proof.
move=> nB cf mB cg; split.
  move: m mB cg;apply: cardinal_c_induction.
    rewrite succ_zero {1} /CNFrv0=> ax1 lt1.
    have h:= (OS_pow OS_omega(proj31_1 lt1)).
    rewrite (osum_expansion1)//; exact:(ord_negl_p6 nB cf lt1).
  move => m mB Hrec cdg ltspd.
  move: OS_omega (BS_succ mB) => oo smB.
  rewrite {1} /CNFrv0 (osum_expansion_succ _ smB).
  have ax2:= (CNFr_ax_p3 smB cdg).
  have o1:= (OS_pow oo (proj1 cdg _ (card_lt_succ smB))).
  apply:(ord_negl_sum1 o1 (OS_CNFr0 smB ax2) (OS_CNFr0 (BS_succ nB) cf)).
  apply: (ord_negl_p6 nB cf ltspd).
  apply: Hrec => //.
  move: (proj2 cdg m mB (card_lt_succ smB)) => la; ord_tac.
move: (CNFr_p2 nB cf) => sa.
rewrite {1}/CNFrv0 (osum_expansion_succ _ mB) => t2.
have ax2:= (CNFr_ax_p3 mB cg).
have o0:= (proj1 cg _ (card_lt_succ mB)).
have o2:= (proj1 cf _ (card_lt_succ nB)).
have o1:= (OS_pow OS_omega o0).
move: (proj1 (ord_negl_sum2 o1 (OS_CNFr0 mB ax2) (proj32 sa) t2)) => le1 {t2}.
case:(ord_negl_lt o1 (proj32 sa) le1);[ move => [] | move => lt0].
   by move:(omega_pow_nz o0).
case:(ord_le_to_ell o0 o2) => // le2; last first.
  move:(CNFr_p4 nB cf le2) => [h _]; ord_tac.
rewrite le2 in le1 lt0; apply: False_ind.
move: nB cf le1 lt0; clear; move => nb; move: n nb.
apply: cardinal_c_induction.
  rewrite succ_zero => h1 _.
  have ha:= (OS_pow OS_omega (proj1 h1 \0o card_lt_01)).
  by rewrite /CNFrv0 osum_expansion1 //; move => [].
move => n nB Hrec ax1.
have snB:= (BS_succ nB).
rewrite /CNFrv0 (osum_expansion_succ _ snB) -/(CNFrv0 _ _) => ha hb.
have ax2:= (CNFr_ax_p3 snB ax1).
have o0:= (proj1 ax1 _ (card_lt_succ snB)).
have o1:= (OS_pow OS_omega o0).
have o3 := (OS_CNFr0 snB ax2).
have hc :=(ord_neg_sum4 o1 o3 ha).
have le0:= (proj2 ax1 _ nB (card_lt_succ snB)).
have le1:= (opow_Momega_le le0).
move: (ord_negl_sum3 o3 le1 hc) => pra.
case: (ord_negl_lt (proj31 le1) o3 pra); last by move /(Hrec ax2 pra).
by move:(omega_pow_nz (proj1 ax2 _ (card_lt_succ nB))) => h1 [].
Qed.

Lemma cantor_of_limit x: limit_ordinal x ->
  exists a n, [/\ ordinalp a, ordinalp n, n <> \0o,
   x = a +o (omega0 ^o n)
   & (a = \0o \/
      [/\ limit_ordinal a, (omega0 ^o n) <=o a
       & cardinal x = cardinal a])].
Proof.
move/limit_ordinal_P3 => [xnz lx].
move: (CNFr_exists (proj32_1 xnz)) => [X ax xv].
move: (CNFr_ax_p2 ax) (CNF_domain1 (proj31 ax)); set n := cardinal _.
move => ax1 nB.
case: (equal_or_not n \0c) => nz.
 by case: xnz; rewrite xv /CNFrv /CNFrv0 -/n nz osum_expansion0.
have cn1: \1c <=c n by apply: card_ge1; fprops.
move : (cdiff_pr cn1) (BS_diff \1c nB); set m := _ -c _ => nv mB.
set f1 := (fun i => omega0 ^o Vg X i).
have ha: ord_below_n f1 (\1c +c m).
   move => i; rewrite nv => lin; apply:(OS_pow OS_omega (proj1 ax1 _ lin)).
have hb: ordinalp (Vg X \0c) by apply:(proj1 ax1); apply/strict_pos_P1.
have hc:= (OS_pow OS_omega hb).
move: xv; rewrite /CNFrv /CNFrv0 -/n -nv.
rewrite (osum_expansionA BS1 mB ha) (osum_expansion1 (f := f1) hc) {2} /f1.
rewrite -/(CNFrv0 _ _); set f2:= (fun i : Set => Vg X (i +c \1c)).
have ax3: CNFr_ax0 f2 m.
  split.
    move => i im; apply: (proj1 ax1); rewrite - nv (csumC \1c).
    apply:csum_Mlteq; fprops.
  move => i iB sim.
  have eq: (succ i +c \1c) = succ (i +c \1c) by rewrite (csum_via_succ1 _ iB).
  rewrite /f2 eq; apply:(proj2 ax1); fprops.
  rewrite - nv (csumC \1c) - eq; apply:csum_Mlteq; fprops.
move:(OS_CNFr0 mB ax3); set a := CNFrv0 f2 m => oa.
case (equal_or_not (Vg X \0o) \0o)=> ez.
  rewrite ez opowx0 (ord_succ_pr oa) => xsa.
  by move:(ord_succ_lt oa); rewrite -xsa; move/lx => []; rewrite - xsa.
exists a,(Vg X \0c);split => //.
case: (equal_or_not m \0c) => mz.
  by left; rewrite /a mz /CNFrv0 osum_expansion0.
have cm1: \1c <=c m by apply: card_ge1; fprops.
move : (cdiff_pr cm1) (BS_diff \1c mB); set m' := _ -c _ => mv m'B.
set f1' := (fun i => omega0 ^o (f2 i)).
have oo1:ordinalp (f1' \0c).
  by apply: (OS_pow OS_omega);apply:(proj1 ax3); apply/strict_pos_P1.
have ha': ord_below_n f1' (\1c +c m').
  move => i il; apply: (OS_pow OS_omega); apply: (proj1 ax3); ue.
have sn0:succ \0c <c n by rewrite - nv succ_zero;apply:finite_sum4_lt; fprops.
have ez0: Vg X \0c <=o Vg X \1c.
  by move: (proj2 ax1 _ BS0 sn0); rewrite succ_zero.
have ez': \0o <o Vg X \1c.
  split; first by move: (ez0) => [h1 h2 _]; ord_tac1.
  move => b; case ez;rewrite - b in ez0; exact: (ord_le0 ez0).
move:(osum_expansionA BS1 m'B ha'); rewrite mv -/(CNFrv0 _ _) -/a.
rewrite (osum_expansion1 oo1); rewrite /f1' /f2 (csum0l CS1) => eq2.
have ax4: CNFr_ax0 (fun z : Set => Vg X ((z +c \1c) +c \1c)) m'.
  split.
    move => i im; apply: (proj1 ax3); rewrite - mv (csumC \1c).
    apply:csum_Mlteq; fprops.
  move => i iB sim.
  have eq: (succ i +c \1c) = succ (i +c \1c) by rewrite (csum_via_succ1 _ iB).
  rewrite /f2 eq; apply:(proj2 ax3); fprops.
  rewrite - mv (csumC \1c) - eq; apply:csum_Mlteq; fprops.
have sa:= (OS_CNFr0 m'B ax4).
have sb:= (opow_Momega_le ez0).
have la: limit_ordinal a.
 rewrite eq2;exact:(osum_limit sa (indecomp_limit2 ez')).
have le3: omega0 ^o Vg X \0c <=o a.
  rewrite eq2; apply: (ord_leT sb); apply: (osum_M0le sa (proj32 sb)).
right; split => //.
rewrite xv (osum_cardinal oa hc).
move: (ordinal_cardinal_le1 le3) => pc.
have pb: omega0 <=o omega0 ^o (Vg X \0c).
   rewrite - {1} (opowx1 OS_omega); apply: opow_Momega_le; fprops.
move: (ordinal_cardinal_le1 (ord_leT pb le3)).
rewrite (card_card CS_omega) => pd.
by apply: sum2_infinite => //; apply: (ge_infinite_infinite omega_infinitec).
Qed.

General Cantor Normal Form


Definition cantor_mon b (e c: fterm) i := (b ^o (e i)) *o (c i).
Definition CNFbv b e c n := osum_expansion (cantor_mon b e c) n.

Definition CNFq_ax b (e c: fterm) n :=
  [/\ \2o <=o b,
    ord_below_n e n,
    (forall i, i <c n -> c i <o b) &
    (forall i, inc i Bnat -> (succ i) <c n -> (e i) <o (e (succ i)))].

Definition CNFb_ax b e c n :=
  CNFq_ax b e c n /\ (forall i, i<c n -> \0o <o c i).

Lemma CNFq_ax_exten b e1 c1 e2 c2 n:
  same_below e1 e2 n -> same_below c1 c2 n ->
  CNFq_ax b e1 c1 n -> CNFq_ax b e2 c2 n.
Proof.
move => h1 h2 [a1 a2 a3 a4]; split => //.
+ by move => i lin; rewrite - (h1 _ lin);apply: a2.
+ by move => i lin; rewrite - (h2 _ lin);apply: a3.
+ move => i iB lin; rewrite - (h1 _ lin).
  by rewrite -(h1 _ (card_le_ltT (card_le_succ iB) lin)); apply:a4.
Qed.

Lemma CNFb_ax_exten b e1 c1 e2 c2 n:
  same_below e1 e2 n -> same_below c1 c2 n ->
  CNFb_ax b e1 c1 n -> CNFb_ax b e2 c2 n.
Proof.
move => h1 h2 [ax1 ax2]; split; first by apply:(CNFq_ax_exten h1 h2 ax1).
by move => i lin; rewrite - (h2 _ lin);apply: ax2.
Qed.

Lemma CNFq_exten b e1 c1 e2 c2 n:
  same_below e1 e2 n -> same_below c1 c2 n ->
  CNFq_ax b e1 c1 n -> inc n Bnat ->
  (CNFq_ax b e2 c2 n /\ CNFbv b e1 c1 n = CNFbv b e2 c2 n).
Proof.
move => h1 h2 ax nB; split; first by apply: (CNFq_ax_exten h1 h2 ax).
apply: (osum_expansion_exten nB) => i lin.
by rewrite /cantor_mon (h1 _ lin) (h2 _ lin).
Qed.

Lemma CNFb_exten b e1 c1 e2 c2 n:
  same_below e1 e2 n -> same_below c1 c2 n ->
  CNFb_ax b e1 c1 n -> inc n Bnat ->
  (CNFb_ax b e2 c2 n /\ CNFbv b e1 c1 n = CNFbv b e2 c2 n).
Proof.
move => h1 h2 [ax1 ax2] nB.
move: (CNFq_exten h1 h2 ax1 nB) => [ax3 ->]; split => //; split => //.
by move => i lin; rewrite - (h2 _ lin);apply: ax2.
Qed.

Lemma CNFq_p0 b e c n i: CNFq_ax b e c n -> i <c n ->
  ordinalp (cantor_mon b e c i).
Proof.
move => [pa pb pc pd] lin.
apply:OS_prod2; first by apply:(OS_pow (proj32 pa) (pb _ lin)).
exact:(proj31_1 (pc _ lin)).
Qed.

Lemma OS_CNFq b e c n : inc n Bnat -> CNFq_ax b e c n ->
  ordinalp (CNFbv b e c n).
Proof.
by move => nb h; apply:(OS_osum_expansion nb) => i /(CNFq_p0 h).
Qed.

Lemma OS_CNFb b e c n : inc n Bnat -> CNFb_ax b e c n ->
  ordinalp (CNFbv b e c n).
Proof. by move => nB [/(OS_CNFq nB) h _]. Qed.

Lemma CNFq_p1 b e c n: inc n Bnat ->
  CNFbv b e c (succ n) = cantor_mon b e c n +o (CNFbv b e c n).
Proof.
by move => nB; rewrite /CNFbv (osum_expansion_succ _ nB).
Qed.

Lemma CNFq_p2 b e c: CNFbv b e c \0c = \0o.
Proof. apply: osum_expansion0. Qed.

Lemma CNFq_p3 b e c n : CNFq_ax b e c n -> \0o <c n ->
   CNFbv b e c \1c = cantor_mon b e c \0c.
Proof. move => h np;apply: osum_expansion1; apply: (CNFq_p0 h np). Qed.

Lemma CNFq_p4 b e c n i: CNFb_ax b e c n -> i <c n ->
  \0o <o cantor_mon b e c i.
Proof.
move => [[pa pb pc pd] pe] lin.
apply: oprod2_pos; last by exact(pe _ lin).
exact:(opow_pos (ord_lt_leT (ord_lt_02) pa) (pb _ lin)).
Qed.

Lemma CNFq_r_ax b e c n m: CNFq_ax b e c n -> m <=c n -> CNFq_ax b e c m.
Proof.
move => [p1 p2 p3 p4] mn; split.
+ done.
+ move => i im; apply: p2; co_tac.
+ move => i im; apply: p3; co_tac.
+ move => i iB im; apply: p4 => //; co_tac.
Qed.

Lemma CNFb_r_ax b e c n m: CNFb_ax b e c n -> m <=c n -> CNFb_ax b e c m.
Proof.
move => [p1 p2] mn; split; first by exact: (CNFq_r_ax p1 mn).
by move => i im; apply: p2; co_tac.
Qed.

Lemma CNFq_p5 b e c n: inc n Bnat -> CNFq_ax b e c (succ n) ->
  CNFq_ax b e c n.
Proof. by move => nB ax; apply:(CNFq_r_ax ax (card_le_succ nB)). Qed.

Lemma CNFb_p5 b e c n: inc n Bnat -> CNFb_ax b e c (succ n) ->
  CNFb_ax b e c n.
Proof. by move => nB ax; apply:(CNFb_r_ax ax (card_le_succ nB)). Qed.

Lemma CNF_exponents_M b e c n: inc n Bnat -> CNFq_ax b e c n ->
  forall i j, i <=c j -> j <c n -> e i <=o e j.
Proof.
move => nB [_ pb _ pc] i j lij ljn.
have lin:=(card_le_ltT lij ljn).
have iB := (BS_lt_int lin nB).
pose r j := e i <=o e j.
have ri:=(ord_leR (pb _ lin)).
case: (equal_or_not n \0c) => nz.
  by move: lin; rewrite nz; move /card_lt0.
move: (cpred_pr nB nz) => [mB mv].
have jcn: j <=c cpred n by apply /(card_lt_succ_leP mB); rewrite -mv.
move: (cardinal_c_induction3 (r := r) iB mB ri)=> h.
apply: (h _ j lij jcn) => k ik kn; rewrite /r => le1.
have kB:= (BS_lt_int kn mB).
have skn : succ k <c n.
  by rewrite mv; apply/(card_lt_succ_leP mB) /(card_le_succ_ltP _ kB).
move:(pc k kB skn) => [lt1 _]; ord_tac.
Qed.

Lemma CNF_exponents_sM b e c n: inc n Bnat -> CNFq_ax b e c n ->
  forall i j, i <c j -> j <c n -> e i <o e j.
Proof.
move => nB ax i j lij ljn.
have iB := (BS_lt_int (card_lt_ltT lij ljn) nB).
move /(card_le_succ_ltP j iB) :lij => leij.
move:(CNF_exponents_M nB ax leij ljn) => sa.
move: ax => [_ _ _ ax]; move: (ax i iB (card_le_ltT leij ljn)) => h; ord_tac.
Qed.

Lemma CNF_exponents_I b e c n: inc n Bnat -> CNFq_ax b e c n ->
  forall i j, i <c n -> j <c n -> e i = e j -> i = j.
Proof.
move => nB /(CNF_exponents_sM nB) => m i j lin ljn eq.
case: (card_le_to_ell (proj31_1 lin) (proj31_1 ljn)) => cp.
+ exact.
+ by move: (proj2 (m i j cp ljn)); rewrite eq.
+ by move: (proj2 (m j i cp lin)); rewrite eq.
Qed.

Lemma CNFq_p6 b e c n: inc n Bnat -> CNFq_ax b e c (succ n) ->
  forall i, i <c n -> e i <o e n.
Proof.
move => nB ax i lin.
exact:(CNF_exponents_sM (BS_succ nB) ax lin (card_lt_succ nB)).
Qed.

Lemma CNFq_p7 b e c n : CNFq_ax b e c n -> \1c <c n ->
   CNFbv b e c \2c = cantor_mon b e c \1c +o cantor_mon b e c \0c.
Proof.
move:card_le_01 => l01 ax n1.
rewrite - succ_one (CNFq_p1 _ _ _ BS1) (CNFq_p3 (n:=n) ax) //; co_tac.
Qed.

Lemma CNFb_p8 b e c n : CNFb_ax b e c (succ n) -> inc n Bnat ->
  ordinalp (e n).
Proof. move => [[_ h _ _] _] nB; exact: (h _ (card_lt_succ nB)). Qed.

Definition cnf_s (f: fterm) k := (fun z => f (z +c k)).
Definition cnf_m (f1 f2:fterm) k := (fun z => Yo (z <c k) (f1 z) (f2 (z -c k))).
Definition cnf_change_n (f:fterm) n x := (fun z => Yo (z=n) x (f z)).

Lemma CNFq_s_ax b e c n k m:
  CNFq_ax b e c m -> (n +c k) <=c m -> inc k Bnat -> inc n Bnat ->
  CNFq_ax b (cnf_s e k) (cnf_s c k) n.
Proof.
move => [p1 p2 p3 p4] mn kB nB.
have aux: forall i, i <c n -> i +c k <c m.
  move => i lin; move:(csum_Mlteq kB nB lin) => h; co_tac.
split.
+ done.
+ by move => i /aux /p2.
+ by move => i /aux /p3.
+ move => i iB /aux; rewrite /cnf_s (csum_via_succ1 _ iB); apply: p4; fprops.
Qed.

Lemma CNFb_s_ax b e c n k m:
  CNFb_ax b e c m -> (n +c k) <=c m -> inc k Bnat -> inc n Bnat ->
  CNFb_ax b (cnf_s e k) (cnf_s c k) n.
Proof.
move => [p1 p2] mn kB nB; split; first by apply: (CNFq_s_ax p1 mn kB nB).
move => i lin; apply: p2; move:(csum_Mlteq kB nB lin) => h; co_tac.
Qed.

Lemma CNFq_m_ax b e1 c1 e2 c2 k m:
   inc k Bnat -> inc m Bnat ->
   CNFq_ax b e1 c1 k -> CNFq_ax b e2 c2 m ->
   (m = \0c \/ e1 (cpred k) <o e2 \0o) ->
   (CNFq_ax b (cnf_m e1 e2 k) (cnf_m c1 c2 k) (k +c m)).
Proof.
move => kB mB [p1 p2 p3 p4] [q1 q2 q3 q4] hs.
have aux: forall i, i <c k +c m -> i <c k \/ (~ (i <c k) /\ (i -c k) <c m).
  move => i lik.
  move:(BS_lt_int lik (BS_sum kB mB))=> iB.
  case: (card_le_to_el (CS_Bnat kB) (CS_Bnat iB)); last by left.
  move => ki; move: (cdiff_pr ki) => s1.
  right; split; first by move=> h; co_tac.
  apply: (csum_lt_simplifiable kB (BS_diff k iB) mB); ue.
rewrite/cnf_m; split.
+ done.
+ move => i /aux []; first by move => ik;by Ytac0; apply: p2.
  by move => [nik likm]; Ytac0; apply: q2.
+ move => i /aux []; first by move => ik;by Ytac0; apply: p3.
  by move => [nik likm]; Ytac0; apply: q3.
+ move => i iB lis; case:(aux _ lis).
    move => lt1; move: (card_le_ltT (card_le_succ iB) lt1) => li1.
    by Ytac0; Ytac0; apply:p4.
  move => [pa pb]; Ytac0.
  case: hs => hs.
    by move: pb; rewrite hs => /card_lt0.
  have ci:= (CS_Bnat iB).
  have ck:= (CS_Bnat kB).
  case: (card_le_to_ell ck (CS_succ i)) => //.
    move:(card_lt_succ iB) => lt1 sa; rewrite sa cdiff_n_n; Ytac0.
    have -> //: i = cpred k by rewrite sa cpred_pr1.
  move => /(card_lt_succ_leP iB) ki.
  case: (aux _ (card_le_ltT (card_le_succ iB) lis));first by move => ik; co_tac.
  move => [pc pd]; Ytac0.
  have sa: succ i -c k = succ (i-c k).
    move: (cdiff_pr4 kB iB BS0 BS1 ki card_le_01).
    rewrite (card_succ_pr4 ci) (csum0r ck) (cdiff_n_0 BS1) card_succ_pr4 //.
    co_tac.
  rewrite sa; apply: (q4 _ (BS_diff k iB)); ue.
Qed.

Lemma CNFb_m_ax b e1 c1 e2 c2 k m:
   inc k Bnat -> inc m Bnat ->
   CNFb_ax b e1 c1 k -> CNFb_ax b e2 c2 m ->
   (m = \0c \/ e1 (cpred k) <o e2 \0o) ->
   (CNFb_ax b (cnf_m e1 e2 k) (cnf_m c1 c2 k) (k +c m)).
Proof.
move => kB mB [p1 p2] [p3 p4] h1.
split; first by apply:CNFq_m_ax.
have aux: forall i, i <c k +c m -> i <c k \/ (~ (i <c k) /\ (i -c k) <c m).
  move => i lik.
  move:(BS_lt_int lik (BS_sum kB mB))=> iB.
  case: (Bnat_to_el kB iB); last by left.
  move => ki; move: (cdiff_pr ki) => s1.
  right; split; first by move=> h; co_tac.
  apply: (csum_lt_simplifiable kB (BS_diff k iB) mB); ue.
move => i /aux []; first by move => ik;by rewrite /cnf_m; Ytac0; apply: p2.
  by move => [nik likm]; rewrite /cnf_m; Ytac0; apply: p4.
Qed.

Lemma CNF_change_n_ax b e c n c0 k:
   \0o <o c0 -> c0 <o b ->
   CNFb_ax b e c n -> CNFb_ax b e (cnf_change_n c k c0) n.
Proof.
move => c1 c2 [[p1 p2 p3 p4] p5]; rewrite/cnf_change_n.
split; [split => // |]; move => i lin; Ytac cc => //; fprops.
Qed.

Definition CNFb_axo := CNFb_ax omega0.
Definition CNFbvo := CNFbv omega0.

Lemma CNFb_change_nv e c n m (c':= (cnf_change_n c n (m +o (c n)))):
  inc n Bnat -> CNFb_axo e c (succ n) -> m <o omega0 ->
  (CNFb_axo e c' (succ n) /\
    CNFbvo e c' (succ n) = omega0 ^o (e n) *o m +o CNFbvo e c (succ n)).
Proof.
move => nB ax mo.
have nn:= card_lt_succ nB.
have om: ordinalp m by ord_tac.
move: (ax) => [ [_ a0 a1 _] a2].
move: (a2 _ nn) => [[_ ocn _] nnz].
have pa:= (osum2_lt_omega mo (a1 _ nn)).
have pb: \0o <o m +o c n.
  apply: ord_ne0_pos; first by ord_tac.
  by move => h; move:(osum2_zero om ocn h) => [_ ]; apply: nesym.
move: (CNF_change_n_ax n pb pa ax); rewrite -/c' => ax2; split; first by exact.
have pc:= (OS_pow OS_omega (a0 _ nn)).
have pd:= CNFq_p0 (proj1 ax) nn.
have pe:=(OS_CNFq nB (CNFq_p5 nB (proj1 ax))).
rewrite /CNFbvo.
rewrite ! (CNFq_p1 _ _ _ nB) (osumA (OS_prod2 pc om) pd pe); apply: f_equal2.
  rewrite /cantor_mon /c' /cnf_change_n; Ytac0.
  by rewrite (osum_prodD om ocn pc).
apply: (osum_expansion_exten nB) => i [_ lin].
by rewrite /cantor_mon /c' /cnf_change_n; Ytac0.
Qed.

Lemma CNFq_A b e c n m:
  inc n Bnat -> inc m Bnat ->
   CNFq_ax b e c (n +c m) ->
   [/\ CNFq_ax b e c n, CNFq_ax b (cnf_s e n) (cnf_s c n) m &
    CNFbv b e c (n +c m) = CNFbv b (cnf_s e n) (cnf_s c n) m +o CNFbv b e c n].
Proof.
move => nB mB ax.
have pa: ord_below_n (cantor_mon b e c) (n +c m).
  by move => i; apply:CNFq_p0.
rewrite /CNFbv (osum_expansionA nB mB pa).
have lt1: n <=c n +c m by apply:csum_M0le; fprops.
have hb: CNFq_ax b e c n by apply:(CNFq_r_ax ax lt1).
have hc //: CNFq_ax b (cnf_s e n) (cnf_s c n) m.
have hh:m +c n <=c n +c m by rewrite csumC;apply: card_leR;co_tac.
exact:(CNFq_s_ax ax hh nB mB).
Qed.

Lemma CNFb_A e c n m:
  inc n Bnat -> inc m Bnat ->
   CNFb_axo e c (n +c m) ->
   [/\ CNFb_axo e c n, CNFb_axo (cnf_s e n) (cnf_s c n) m &
    CNFbvo e c (n +c m) = CNFbvo (cnf_s e n) (cnf_s c n) m +o CNFbvo e c n].
Proof.
move => nB mB ax.
rewrite /CNFb_axo /CNFbvo.
have pa: ord_below_n (cantor_mon omega0 e c) (n +c m).
  by move:ax => [ax _] i; apply:CNFq_p0.
rewrite /CNFbv (osum_expansionA nB mB pa).
have lt1: n <=c n +c m by apply:csum_M0le; fprops.
have hb: CNFb_ax omega0 e c n by apply:(CNFb_r_ax ax lt1).
rewrite csumC in pa.
have hc //: CNFb_ax omega0 (cnf_s e n) (cnf_s c n) m.
have hh:m +c n <=c n +c m by rewrite csumC;apply: card_leR;co_tac.
exact:(CNFb_s_ax ax hh nB mB).
Qed.

Lemma CNFq_Al b e c n (e1 := fun z => e (succ z)) (c1 := fun z => c (succ z)):
  inc n Bnat ->
   CNFq_ax b e c (succ n) ->
   [/\ ordinalp(cantor_mon b e c \0c), CNFq_ax b e1 c1 n &
    CNFbv b e c (succ n) = CNFbv b e1 c1 n +o (cantor_mon b e c \0c)].
Proof.
move => nB ax.
have h1: forall i, i<c n -> (cnf_s e \1c) i = e1 i.
  move => i lin; rewrite /cnf_s /e1 card_succ_pr4 //; co_tac.
have h2: forall i, i<c n -> (cnf_s c \1c) i = c1 i.
  by move => i lin; rewrite /cnf_s /c1 card_succ_pr4 //; co_tac.
have eq1: succ n = \1c +c n by rewrite csumC; exact:(Bsucc_rw nB).
rewrite eq1 in ax; move: (CNFq_A BS1 nB ax) => [sa sb sc].
have lt1: \0c <c (\1c +c n) by rewrite - eq1; apply: succ_positive.
rewrite eq1 sc (CNFq_p3 ax lt1).
move:(CNFq_exten h1 h2 sb nB) => [ax1 ->]; split => //.
by apply:(CNFq_p0 ax lt1).
Qed.

Lemma CNFb_Al e c n (e1 := fun z => e (succ z)) (c1 := fun z => c (succ z)):
  inc n Bnat ->
   CNFb_axo e c (succ n) ->
   [/\ ordinalp(cantor_mon omega0 e c \0o), CNFb_axo e1 c1 n &
    CNFbvo e c (succ n) = CNFbvo e1 c1 n +o (cantor_mon omega0 e c \0o)].
Proof.
move => nB [ax1 ax2]; move:(CNFq_Al nB ax1) => [sa sb sc]; split => //.
split => // i lin; apply: ax2.
by apply/(card_succ_succ_ltP (BS_lt_int lin nB) nB).
Qed.

Lemma CNFq_pg0 b e c n: CNFq_ax b e c n -> \0o <o b.
Proof. move => [pb _ _ _]; move: ord_lt_02 => h; ord_tac. Qed.

Lemma CNFq_pg1 b e c n: inc n Bnat -> CNFq_ax b e c (succ n) ->
  CNFbv b e c (succ n) <o (b ^o (succ_o (e n))).
Proof.
move: n;apply: cardinal_c_induction.
   move => h; rewrite succ_zero (CNFq_p3 h (succ_positive \0c)).
   move: (h) => [bo ax1 ax2 _ ].
   have szp:=(succ_positive \0c).
   move: (CNFq_pg0 h) (ax1 _ szp) (ax2 _ szp) => bp eo cb.
   rewrite /cantor_mon (opow_succ (proj32_1 bp) eo).
   exact:(oprod_Meqlt cb (opow_pos bp eo)).
move=> n nB Hrec ax1.
move: (Hrec (CNFq_p5 (BS_succ nB) ax1)) => sa.
move: (ax1) => [b2 _ ax3 ax2].
have snn:= (card_lt_succ (BS_succ nB)).
move: (ax2 n nB snn) (CNFq_p0 ax1 snn) => /ord_succ_ltP lt1 o1.
move:(ord_lt_leT sa (opow_Meqle (CNFq_pg0 ax1) lt1)) => lt2.
have ha: ordinalp (b ^o e (succ n)) by ord_tac.
move: (ax3 _ snn) => cm1.
have hb: ordinalp (c (succ n)) by ord_tac.
have lt3: b ^o e (succ n) *o succ_o (c (succ n)) <=o b ^o succ_o (e (succ n)).
  rewrite opow_succ; try ord_tac; apply: oprod_Meqle; last by ord_tac.
  by move /ord_succ_ltP: cm1.
move: (osum_Meqlt lt2 o1).
rewrite (CNFq_p1 _ _ _ (BS_succ nB)) {2}/cantor_mon - (oprod2_succ ha hb) => h1.
ord_tac.
Qed.

Lemma CNFq_pg2 b e c n a: inc n Bnat -> CNFq_ax b e c (succ n) ->
  (e n) <o a -> CNFbv b e c (succ n) <o (b ^o a).
Proof.
move => pa pb pc; move: (CNFq_pg1 pa pb) => h.
move/ord_succ_ltP: pc => pc.
exact: (ord_lt_leT h (opow_Meqle (CNFq_pg0 pb) pc)).
Qed.

Lemma CNFq_pg3 b e c n a: inc n Bnat -> CNFq_ax b e c n -> ordinalp a ->
  (forall i, i <c n -> e i <o a) ->
  CNFbv b e c n <o (b ^o a).
Proof.
move => nB ax oa h; move: (CNFq_pg0 ax) => bp.
case: (equal_or_not n \0c) => nz.
  rewrite nz CNFq_p2 //; apply: opow_pos; fprops.
move: (cpred_pr nB nz) => [qa qb].
rewrite qb in h ax.
have sa: (e (cpred n)) <o a by apply: h; apply: (card_lt_succ qa).
rewrite qb; exact:(CNFq_pg2 qa ax).
Qed.

Lemma CNFq_pg b e c n: inc n Bnat -> CNFq_ax b e c (succ n) ->
  CNFbv b e c n <o (b ^o (e n)).
move => nB ax.
move: (ax) => [_ ax2 _ _].
exact:(CNFq_pg3 nB (CNFq_p5 nB ax) (ax2 _ (card_lt_succ nB)) (CNFq_p6 nB ax)).
Qed.

Lemma CNFq_pg4 b e c n: inc n Bnat -> CNFb_ax b e c (succ n) ->
  (b ^o (e n)) <=o CNFbv b e c (succ n).
Proof.
move => nB [ax ax2]; move: (ax) => [b2 ax3 _ _].
have nn:= (card_lt_succ nB).
have pa:= (oprod_Mle1 (OS_pow (proj32 b2) (ax3 _ nn)) (ax2 _ nn)).
rewrite (CNFq_p1 _ _ _ nB).
move: (osum_Mle0 (proj32 pa) (OS_CNFq nB (CNFq_p5 nB ax))) => pb.
ord_tac.
Qed.

Lemma CNFq_pg5 b e c n: inc n Bnat -> CNFb_ax b e c (succ n) ->
  \0o <o CNFbv b e c (succ n).
Proof.
move => nB ax.
move: (opow_pos (CNFq_pg0 (proj1 ax)) (CNFb_p8 ax nB)) => cp.
exact:(ord_lt_leT cp (CNFq_pg4 nB ax)).
Qed.

Lemma CNFb_unique b e1 c1 n1 e2 c2 n2:
  inc n1 Bnat -> inc n2 Bnat -> CNFb_ax b e1 c1 n1 -> CNFb_ax b e2 c2 n2 ->
  CNFbv b e1 c1 n1 = CNFbv b e2 c2 n2 ->
  (n1 = n2 /\ forall i, i <c n1 -> e1 i = e2 i /\ c1 i = c2 i).
Proof.
move => n1B n2B; move: n1 n1B n2 n2B; apply: cardinal_c_induction.
  move=> n2 n2B ax1 ax2.
  case: (equal_or_not n2 \0c) => n2z.
    by rewrite n2z => _; split => // i /card_lt0.
  move: (cpred_pr n2B n2z) => [mb mv] eq.
  rewrite mv in ax2 eq.
    by move: (proj2 (CNFq_pg5 mb ax2)); rewrite - eq CNFq_p2;case.
move =>n1 n1B Hrec n2' n2'B ax1s ax2s sv.
case: (equal_or_not n2' \0c) => n2z.
  by move: (proj2 (CNFq_pg5 n1B ax1s)); rewrite sv n2z CNFq_p2; case.
move: (cpred_pr n2'B n2z) => []; set n2 := (cpred n2'); move => n2B n2v.
rewrite n2v in ax2s sv.
move: (card_lt_succ n1B) (card_lt_succ n2B) => lt1 lt2.
move: (ax1s) (ax2s) => [[u1 u2 u3 u4] u5] [[v1 v2 v3 v4] v5].
move: (u2 n1 lt1) (v2 n2 lt2) (u3 n1 lt1) (v3 n2 lt2) => oa1 oa2 of1 of2.
move: (u5 n1 lt1) (v5 n2 lt2) => ob1 ob2.
move: (proj32_1 ob1) (proj32_1 ob2) => oc1 oc2.
move: (CNFb_p5 n1B ax1s)(CNFb_p5 n2B ax2s) => ax1 ax2.
move: (OS_CNFq n1B (proj1 ax1)) (OS_CNFq n2B (proj1 ax2)) => oe1 oe2.
set a1 := CNFbv b e1 c1 (succ n1).
set a2 := CNFbv b e2 c2 (succ n2).
move: (erefl a1); rewrite {2} /a1 (CNFq_p1 _ _ _ n1B) => ha1.
move: (erefl a2); rewrite {2} /a2 (CNFq_p1 _ _ _ n2B) => ha2.
move: (CNFq_pg n1B (proj1 ax1s)) (CNFq_pg n2B (proj1 ax2s)) => hb1 hb2.
have og1:=(OS_CNFq (BS_succ n1B) (proj1 ax1s)).
have ox1: (ord_ext_div_pr b a1 (e1 n1) (c1 n1) (CNFbv b e1 c1 n1)).
  split;[ exact | exact| exact | split; [exact: ha1 | exact | exact | exact]].
have ox2: (ord_ext_div_pr b a1 (e2 n2) (c2 n2) (CNFbv b e2 c2 n2)).
  rewrite /a1 sv -/a2.
  split;[ exact | exact| exact | split; [exact: ha2 | exact | exact | exact]].
move: (ord_ext_div_unique u1 og1 ox1 ox2) => [se1 sc1 sr1].
move: (Hrec _ n2B ax1 ax2 sr1) => [n12 etc]; split.
   by rewrite n2v - n12.
move => i /(card_lt_succ_leP n1B); case: (equal_or_not i n1) => in1.
   by rewrite in1 {4 6} n12 => _; split.
by move => nin1; move: (etc _ (conj nin1 in1)).
Qed.

Lemma CNF_singleton b c0 e0 (c:= fun _: Set => c0)(e:= fun _: Set => e0):
  ordinalp e0 -> c0 <o b -> \2c <=o b ->
  [/\ CNFq_ax b e c \1c, CNFbv b e c \1c = b ^o e0 *o c0 &
    (\0o <o c0 -> CNFb_ax b e c \1c)].
Proof.
move=> oe co b2.
have aux: CNFq_ax b e c \1c.
  rewrite /e/c;split => // i ib.
  move: (@succ_positive i) => ha.
  rewrite - succ_zero;move/(card_lt_succ_leP BS0) => hb; co_tac.
by rewrite (CNFq_p3 aux card_lt_01).
Qed.

Lemma CNF_exp_bnd b e c n e0:
  \2c <=o b -> ordinalp e0 -> inc n Bnat ->
  CNFb_ax b e c n -> CNFbv b e c n <o b ^o e0 ->
  (forall i, i <c n -> e i <o e0).
Proof.
move => le1 oe nB ax lt1 i id.
case: (equal_or_not n \0c) => nz.
  by move: id; rewrite nz => /card_lt0.
move: (cpred_pr nB nz) => [mB mv].
rewrite mv in ax id.
move/(card_lt_succ_leP mB): id => ipn.
move:(CNF_exponents_M (BS_succ mB) (proj1 ax) ipn (card_lt_succ mB)) => ha.
case (ord_le_to_el oe (proj31 ha)) => // hb.
move:(CNFq_pg4 mB ax); rewrite - mv => h1; move:(ord_le_ltT h1 lt1) => hc.
move: (opow_Meqle (ord_lt_leT ord_lt_02 le1) (ord_leT hb ha)) => hd; ord_tac.
Qed.

Lemma CNFb_exists a b:
  ordinalp a -> \2c <=o b ->
  exists e c n, [/\ CNFb_ax b e c n, inc n Bnat & a = CNFbv b e c n].
Proof.
move=> oa oeb.
pose p a := exists e c n, [/\ CNFb_ax b e c n, inc n Bnat & a = CNFbv b e c n].
apply: (least_ordinal2 (p := p)) oa => y oy p2.
case: (equal_or_not y \0o) => ynz.
  exists (@id Set), (@id Set), \0c; split; first split.
  + split => // i; try move /card_lt0 => //.
    by move => _ /card_lt0.
  + by move => i /card_lt0.
  + fprops.
  + by rewrite CNFq_p2.
move: OS2 => o2.
have cpy1: \0o <o y by ord_tac1.
move: (ord_ext_div_exists oeb cpy1)
  => [e1 [c1 [rm [oe1 oc1 or [eq1 lt1 lt2 lt3]]]]].
have rmy: rm <o y.
  have p5: ordinalp (b ^o e1) by apply: OS_pow => //; ord_tac.
  have p3: (b ^o e1) <=o ((b ^o e1) *o c1) by apply: oprod_Mle1.
  move: (osum_Mle0 (proj32 p3) or); rewrite - eq1 => p4.
  exact : (ord_lt_leT lt1 (ord_leT p3 p4)).
move: (p2 _ rmy) => [e [c [n [ax nB rv]]]].
have snB:= (BS_succ nB).
pose e' i := Yo (i = n) e1 (e i).
pose c' i := Yo (i = n) c1 (c i).
exists e', c', (succ n).
have ha: CNFbv b e' c' n = CNFbv b e c n.
   rewrite /CNFbv; apply: osum_expansion_exten => // i [ _ lin].
   by rewrite /e' /c' /cantor_mon; Ytac0; Ytac0.
have hb: cantor_mon b e' c' n = b ^o e1 *o c1.
  by rewrite /cantor_mon /e' /c'; Ytac0; Ytac0.
rewrite (CNFq_p1 _ _ _ nB) ha - rv hb - eq1.
suff: CNFb_ax b e' c' (succ n) by move => hc; split.
move: (CNF_exp_bnd oeb oe1 nB ax); rewrite - rv => hx.
move: ax => [[a1 a2 a3 a4] a5]; split; last first.
  rewrite /c';move => i /(card_lt_succ_leP nB); case (equal_or_not i n).
    by move => -> _; Ytac0.
  move => i1 i2; Ytac0; exact: (a5 _ (conj i2 i1)).
split; first done.
+ rewrite /e';move => i /(card_lt_succ_leP nB); case (equal_or_not i n).
    by move => -> _; Ytac0.
  move => i1 i2; Ytac0; exact: (a2 _ (conj i2 i1)).
+ rewrite /c';move => i /(card_lt_succ_leP nB); case (equal_or_not i n).
    by move => -> _; Ytac0.
  move => i1 i2; Ytac0; exact: (a3 _ (conj i2 i1)).
+ rewrite /e';move => i iB /(card_lt_succ_leP nB) sin.
  have isi:= (card_lt_leT (card_lt_succ iB) sin).
  move: (proj2 isi) => nin; Ytac0.
  case (equal_or_not (succ i) n) => ne1; Ytac0; first by exact: (hx lt1 _ isi).
  exact:(a4 i iB (conj sin ne1)).
Qed.

Definition CNFbB n e c :=
   J n (J (Lg Bnat (fun z => Yo (z <c n) (e z) \0o))
          (Lg Bnat (fun z => Yo (z <c n) (c z) \0o))).

Section CNF_baseb.
Variable (b: Set).
Hypothesis bg2: \2o <=o b.

Definition cnfb_ax X :=
  [/\ pairp X, inc (P X) Bnat, pairp (Q X) &
   [/\ fgraph (P (Q X)), fgraph (Q (Q X)), domain (P (Q X)) = Bnat,
      domain (Q (Q X)) = Bnat &
   [/\ (forall i, inc i Bnat -> (P X) <=c i -> Vg (P (Q X)) i = \0o),
       (forall i, inc i Bnat -> (P X) <=c i -> Vg (Q (Q X)) i = \0o) &
    CNFb_ax b (Vg (P (Q X))) (Vg (Q (Q X))) (P X)]]].

Definition cnfbv X := CNFbv b (Vg (P (Q X))) (Vg (Q (Q X))) (P X).

Definition cnfb_bound x:=
   Bnat \times ((gfunctions Bnat (succ_o x)) \times (gfunctions Bnat b)).

Definition the_cnf x :=
  select (fun X => cnfb_ax X /\ cnfbv X = x) (cnfb_bound x).

Definition the_cnf_degree x :=
  (Vg (P (Q (the_cnf x))) (cpred (P (the_cnf x)))).

Lemma props_of_b: [/\ ordinalp b, b <> \0o & \0o <o b].
Proof.
move: bg2 => [_ ob _].
move: (ord_lt_leT (ord_lt_02) bg2) => bp.
move: (bp) => [_ /nesym bne].
split => //.
Qed.

Lemma cnfb_unique X1 X2: cnfb_ax X1 -> cnfb_ax X2 ->
   cnfbv X1 = cnfbv X2 -> X1 = X2.
Proof.
move => [p1 p2 p3 [p4 p5 p6 p7 [p8 p9 p10]]].
move => [q1 q2 q3 [q4 q5 q6 q7 [q8 q9 q10]]] sv.
move:(CNFb_unique p2 q2 p10 q10 sv) =>[sa sb].
have ha: P (Q X1) = P (Q X2).
  apply: fgraph_exten; [exact | exact | by rewrite p6 q6 | move => i id /=].
  rewrite p6 in id.
  case (card_le_to_el (CS_Bnat p2) (CS_Bnat id)) => h.
    by rewrite (p8 _ id h) (q8 _ id) - ?sa.
  exact (proj1 (sb _ h)).
have hb: Q (Q X1) = Q (Q X2).
  apply: fgraph_exten; [exact | exact | by rewrite p7 q7 | move => i id /=].
  rewrite p7 in id.
  case (card_le_to_el (CS_Bnat p2) (CS_Bnat id)) => h.
    by rewrite (p9 _ id h) (q9 _ id) - ?sa.
  exact (proj2 (sb _ h)).
have hc: Q X1 = Q X2 by apply:pair_exten.
by apply:pair_exten.
Qed.

Lemma cnfb_bound_p X: cnfb_ax X -> inc X (cnfb_bound (cnfbv X)).
Proof.
move => [p1 p2 p3 [p4 p5 p6 p7 [p8 p9 p10]]].
move: (p10) => [[p11 p12 p13 p14] p15].
move: (props_of_b) => [ob bnz bp].
apply/setX_P; split => //; apply/setX_P; split =>//.
  move: (OS_CNFq p2 (proj1 p10)) => osx.
  apply/graphset_P2; split => // v /(range_gP p4) [i]; rewrite p6 => iB ->.
  apply/(ord_leP osx).
  case: (Bnat_to_el p2 iB) => h; first by rewrite (p8 i iB h); ord_tac1.
  have ot:= (p12 _ h).
  move:(ord_leT (oprod_M1le bp ot)(opow_Mspec2 ot bg2)).
  move => ha; apply: (ord_leT ha).
  case: (equal_or_not (P X) \0c) => nz.
    by move: h; rewrite nz => /card_lt0.
  move: (cpred_pr p2 nz) => [mB mv].
  rewrite mv in p10 h.
  move:(CNFq_pg4 mB p10); rewrite - mv => eq1.
  have aa: i <=c (cpred (P X)) by apply /(card_lt_succ_leP mB).
  have bb: (cpred (P X)) <c succ (cpred (P X)) by apply:(card_lt_succ mB).
  move: (CNF_exponents_M (BS_succ mB) (proj1 p10) aa bb) => /(opow_Meqle bp).
  move => hc; ord_tac.
apply/graphset_P2; split => // v /(range_gP p5) [i]; rewrite p7 => iB ->.
case: (card_le_to_el (CS_Bnat p2) (CS_Bnat iB)) => h.
  by rewrite (p9 i iB h); apply: ord_ne_has0.
by move: (p13 i h) => /(ord_ltP ob).
Qed.

Lemma CNFbB_prop n e c: CNFb_ax b e c n -> inc n Bnat ->
   cnfb_ax (CNFbB n e c) /\ cnfbv (CNFbB n e c) = CNFbv b e c n.
Proof.
move => ax nB.
move:(ax) => [[a1 a2 a3 a4] a5]; rewrite /CNFbB.
rewrite /cnfb_ax /cnfbv !(pr1_pair, pr2_pair).
set e1 := Lg _ _.
set c1 := Lg _ _.
have h1: same_below e (Vg e1) n.
  by move => i lin; rewrite /e1 (LVg_E _ (BS_lt_int lin nB)); Ytac0.
have h2: same_below c (Vg c1) n.
  by move => i lin; rewrite /c1 (LVg_E _ (BS_lt_int lin nB)); Ytac0.
move: (CNFb_exten h1 h2 ax nB) => [sa <-].
split => //; split; [fprops | exact | fprops |].
rewrite {1 2 3} /e1 {1 2 3} /c1; bw; split; fprops; split => //.
  move => i iB ni; bw; Ytac h; [ co_tac | done].
move => i iB ni; bw; Ytac h; [ co_tac | done].
Qed.

Lemma cnfb_exists x: ordinalp x ->
   exists2 X, cnfb_ax X & cnfbv X = x.
Proof.
move => ox.
move: (CNFb_exists ox bg2) => [e [c [n [ax nB ->]]]].
by move: (CNFbB_prop ax nB) => [sa sb];exists (CNFbB n e c).
Qed.

Lemma the_cnf_p0 x (X:= the_cnf x): ordinalp x -> cnfb_ax X /\ cnfbv X = x.
Proof.
move => ox.
suff: (cnfb_ax X /\ cnfbv X = x) /\ inc X (cnfb_bound x) by move => [].
apply: select_pr.
  move: (cnfb_exists ox) => [z za zb]; exists z => //.
  by rewrite - zb ; apply:cnfb_bound_p.
move => a b' _ [p1 p2] _ [p3 p4].
rewrite - p4 in p2;exact:(cnfb_unique p1 p3 p2).
Qed.

Lemma cnfb_ax_simp X: cnfb_ax X ->
  inc (P X) Bnat /\ CNFb_ax b (Vg (P (Q X))) (Vg (Q (Q X))) (P X).
Proof. by move => [ _ p2 _ [_ _ _ _ [_ _ p10]]]. Qed.

Lemma the_cnf_p1: P(the_cnf \0o) = \0c.
Proof.
move:(the_cnf_p0 OS0) => [/cnfb_ax_simp [p2 p10] sb].
ex_middle nz.
move: (cpred_pr p2 nz) => [mB mv].
rewrite mv in p10.
by move:(proj2 (CNFq_pg5 mB p10)); rewrite -mv -/(cnfbv _) sb.
Qed.

Lemma the_cnf_p2 x (m := (cpred (P (the_cnf x)))): \0o <o x ->
  inc m Bnat /\ P (the_cnf x) = succ m.
Proof.
move => [[ _ ox _] xnz].
move:(the_cnf_p0 ox) => [/cnfb_ax_simp [nB ax] sb].
case (equal_or_not (P (the_cnf x)) \0c) => nz.
  by move:xnz; rewrite - sb /cnfbv nz /CNFbvo CNFq_p2.
exact: (cpred_pr nB nz).
Qed.

Lemma the_cnf_p3 e c n: CNFb_ax b e c n -> inc n Bnat ->
  the_cnf (CNFbv b e c n) = (CNFbB n e c).
Proof.
move => ax nB.
have ox:=(OS_CNFq nB (proj1 ax)).
move:(CNFbB_prop ax nB) (the_cnf_p0 ox) => [sa sb] [sc sd].
rewrite - sd in sb.
symmetry; exact: (cnfb_unique sa sc sb).
Qed.

Lemma OS_the_cnf_degree x: ordinalp x -> ordinalp (the_cnf_degree x).
Proof.
move => /the_cnf_p0 [sa sb].
move: sa => [p1 p2 p3 [p4 p5 p6 p7 [p8 p9 p10]]].
rewrite /the_cnf_degree.
case (equal_or_not (P (the_cnf x)) \0c) => nz.
  rewrite nz /cpred setU_0 -/card_zero p8; fprops; rewrite nz; fprops.
move: (p10) => [[_ p12 _ _] _]; apply: p12.
move:(cpred_pr p2 nz) => [mB eq]; rewrite {2} eq; apply: (card_lt_succ mB).
Qed.

End CNF_baseb.

Definition CNFB_ax X :=
  [/\ pairp X, inc (P X) Bnat, pairp (Q X) &
   [/\ fgraph (P (Q X)), fgraph (Q (Q X)), domain (P (Q X)) = Bnat,
      domain (Q (Q X)) = Bnat &
   [/\ (forall i, inc i Bnat -> (P X) <=c i -> Vg (P (Q X)) i = \0o),
       (forall i, inc i Bnat -> (P X) <=c i -> Vg (Q (Q X)) i = \0o) &
    CNFb_axo (Vg (P (Q X))) (Vg (Q (Q X))) (P X)]]].

Definition CNFBv X := CNFbvo (Vg (P (Q X))) (Vg (Q (Q X))) (P X).

Definition CNFB_bound x:=
   Bnat \times ((gfunctions Bnat (succ_o x)) \times (gfunctions Bnat Bnat)).

Definition the_CNF x :=
  select (fun X => CNFB_ax X /\ CNFBv X = x) (CNFB_bound x).
Definition the_CNF_degree x :=
  (Vg (P (Q (the_CNF x))) (cpred (P (the_CNF x)))).

Lemma CNFB_unique X1 X2: CNFB_ax X1 -> CNFB_ax X2 ->
   CNFBv X1 = CNFBv X2 -> X1 = X2.
Proof. by apply:cnfb_unique. Qed.

Lemma CNFB_bound_p X: CNFB_ax X -> inc X (CNFB_bound (CNFBv X)).
Proof. apply:(cnfb_bound_p ord_le_2omega). Qed.

Lemma CNFB_exists x: ordinalp x ->
   exists2 X, CNFB_ax X & CNFBv X = x.
Proof. apply: (cnfb_exists ord_le_2omega).
Qed.

Lemma the_CNF_p0 x (X:= the_CNF x): ordinalp x -> CNFB_ax X /\ CNFBv X = x.
Proof. exact: (the_cnf_p0 ord_le_2omega). Qed.

Lemma CNFB_ax_simp X: CNFB_ax X ->
  inc (P X) Bnat /\ CNFb_axo (Vg (P (Q X))) (Vg (Q (Q X))) (P X).
Proof. by move => [ _ p2 _ [_ _ _ _ [_ _ p10]]]. Qed.

Lemma the_CNF_p1: P(the_CNF \0o) = \0c.
Proof. exact: (the_cnf_p1 ord_le_2omega). Qed.

Lemma the_CNF_p2 x (m := (cpred (P (the_CNF x)))): \0o <o x ->
  inc m Bnat /\ P (the_CNF x) = succ m.
Proof. exact: (the_cnf_p2 ord_le_2omega). Qed.

Lemma the_CNF_p3 e c n: CNFb_axo e c n -> inc n Bnat ->
  the_CNF (CNFbvo e c n) = (CNFbB n e c).
Proof. exact: (the_cnf_p3 ord_le_2omega). Qed.

Lemma OS_the_CNF_degree x: ordinalp x -> ordinalp (the_CNF_degree x).
Proof. exact: (OS_the_cnf_degree ord_le_2omega). Qed.

Lemma the_CNF_p4 x (n := the_CNF_degree x): \0o <o x ->
  omega0 ^o n <=o x /\ x <o omega0 ^o (succ_o n).
Proof.
move => xp; move: (the_CNF_p2 xp) => [].
set m := cpred _; move => mB mv.
move: xp => [[ _ ox _] xnz].
move:(the_CNF_p0 ox) => [/CNFB_ax_simp [nB ax] sb].
rewrite mv in ax; split.
  move:(CNFq_pg4 mB ax);rewrite -mv -/CNFbvo -/(CNFBv _) sb //.
move:(CNFq_pg1 mB (proj1 ax));rewrite -mv -/CNFbvo -/(CNFBv _) sb //.
Qed.

Lemma the_CNF_p5 e c n (x:= CNFbvo e c (succ n)):
  inc n Bnat -> CNFb_axo e c (succ n) ->
  \0o <o x /\ the_CNF_degree x = e n.
Proof.
move => nB ax.
move:(CNFq_pg5 nB ax) => xp.
split; first by exact.
move:(the_CNF_p2 xp).
rewrite /x /the_CNF_degree; rewrite(the_CNF_p3 ax (BS_succ nB)).
set m := (cpred (P (CNFbB (succ n) e c))); move => [mB].
have sm:= card_lt_succ mB.
rewrite /CNFbB; aw; move => /(succ_injective1 (CS_Bnat nB) (CS_Bnat mB)) ->.
by bw; Ytac0.
Qed.

Lemma the_CNF_degree_one: the_CNF_degree \1o = \0o.
Proof.
move:(proj1 (the_CNF_p4 ord_lt_01))(OS_the_CNF_degree OS1).
set n := the_CNF_degree \1o.
rewrite -(@opowx0 omega0) => sa sb.
case: (ord_zero_dichot sb) => // /opow_Momega_lt sc; ord_tac.
Qed.

Lemma the_CNF_degree_omega: the_CNF_degree omega0 = \1o.
Proof.
move:(the_CNF_p4 ord_lt_0omega)(OS_the_CNF_degree OS_omega).
set n := the_CNF_degree omega0.
rewrite -{2 3} (opowx1 OS_omega); move => [sa sb] sc.
case: (ord_le_to_el sc OS1); last by move /opow_Momega_lt => h; ord_tac.
move => pa; case (equal_or_not n \1o) => n1//.
by case: (proj2 sb); rewrite (ord_lt1 (conj pa n1)) succ_o_zero.
Qed.

Lemma the_CNF_split x (e:= the_CNF_degree x) (X:= (the_CNF x))
    (c:= Vg (Q (Q X)) (cpred (P X)))
    (r := CNFbvo (Vg (P (Q X))) (Vg (Q (Q X))) (cpred (P X))):
  \0o <o x -> [/\ \0o <o c, c <o omega0, r <o omega0 ^o e &
    x = omega0 ^o e *o c +o r].
Proof.
move => xp.
move:(the_CNF_p2 xp).
rewrite /e/c/r/X. set m := cpred _; move => [mB Pv].
move: (the_CNF_p0 (proj32_1 xp)) => [/CNFB_ax_simp [sa]].
rewrite /CNFBv Pv.
set E := (Vg (P (Q (the_CNF x)))); set C := (Vg (Q (Q (the_CNF x)))).
move => sb sc.
move: (sb) => [[a1 a2 a3 a4] a5].
split.
+ exact:(a5 _ (card_lt_succ mB)).
+ exact:(a3 _ (card_lt_succ mB)).
+ exact:(CNFq_pg mB (proj1 sb)).
+ by rewrite -{1} sc /CNFbvo (CNFq_p1 _ _ _ mB).
Qed.

Lemma the_CNF_p6 a e: ordinalp e -> \0o <o a -> a <o (omega0 ^o e) ->
  (the_CNF_degree a) <o e.
Proof.
move => oe ap lta.
have lt2:= (ord_le_ltT (proj1 (the_CNF_p4 ap)) lta).
have od:= (OS_the_CNF_degree (proj32_1 ap)).
by rewrite (opow_Meqltr ord_le_2omega od oe).
Qed.

Lemma ord_negl_p8 ep cp p en cn n:
  CNFb_axo ep cp (succ p) -> CNFb_axo en cn (succ n) ->
  inc p Bnat -> inc n Bnat -> ep p <o en n ->
  (CNFbvo ep cp (succ p)) <<o (CNFbvo en cn (succ n)).
Proof.
move => axp axn pB nB ln.
have snB:= BS_succ nB.
set x := CNFbvo en cn (succ n).
have aux: forall e c,
   c <o omega0 -> e <o (en n) -> ((omega0 ^o e) *o c) <<o x.
  move => e c cb ef; rewrite /x.
  have oe: ordinalp e by ord_tac.
  have oc: ordinalp c by ord_tac.
  have om:=(CNFq_p0 (proj1 axn) (card_lt_succ nB)).
  rewrite /CNFbvo (CNFq_p1 _ _ _ nB).
  apply: (ord_negl_sum (OS_prod2 (OS_pow OS_omega oe) oc) om).
     exact: (OS_CNFq nB (CNFq_p5 nB (proj1 axn))).
   apply: (ord_negl_p5 cb ef) (proj2 axn _ (card_lt_succ nB)).
move:p pB axp ln; apply: cardinal_c_induction.
  move => ax1 lt1; move:(ax1) => [[_ _ hc _] _].
  rewrite /CNFbvo succ_zero (CNFq_p3 (proj1 ax1) (succ_positive \0c)).
  exact: (aux _ _ (hc _ (succ_positive \0c)) lt1).
move => p pB Hrec ax1 l1.
have spB:= (BS_succ pB).
move: (CNFb_p5 spB ax1) => axr.
have spp:= (card_lt_succ spB).
move:(proj1 ax1) => [_ _ hc ha].
have h0:= (ord_lt_leT (ha p pB spp) (proj1 l1)).
have h1 :=(CNFq_p0 (proj1 ax1) spp).
have h6: cp (succ p) <o omega0 by apply: (hc _ spp).
have ox: ordinalp x by apply:OS_CNFb.
rewrite /CNFbvo (CNFq_p1 _ _ _ spB).
exact: (ord_negl_sum1 h1 (OS_CNFb spB axr) ox (aux _ _ h6 l1) (Hrec axr h0)).
Qed.

Lemma ord_negl_p7 x y: \0o <o x -> \0o <o y ->
  the_CNF_degree x <o the_CNF_degree y -> x <<o y.
Proof.
move => xp yp.
move:(the_CNF_p2 xp)(the_CNF_p2 yp).
move: (the_CNF_p0 (proj32_1 xp))(the_CNF_p0 (proj32_1 yp)).
rewrite /the_CNF_degree.
set p:= (cpred _); set q := (cpred _).
move => [/CNFB_ax_simp ax1 v1][/CNFB_ax_simp ax2 v2] [pB sp][nB sn] h.
rewrite sp in ax1; rewrite sn in ax2.
move: (ord_negl_p8 (proj2 ax1) (proj2 ax2) pB nB h).
by rewrite - sp - sn -/(CNFBv _) v1 -/(CNFBv _) v2.
Qed.

Lemma CNF_sum_pr1 eX cX eY cY n m p
  (eZ := cnf_m eY (cnf_s eX n) (succ p))
  (cZ := cnf_m cY (cnf_s cX n) (succ p)):
  inc n Bnat -> inc m Bnat -> inc p Bnat ->
  CNFb_axo eX cX (n +c m) ->
  CNFb_axo eY cY (succ p) ->
  eY p <o eX n -> (n = \0c \/ eX (cpred n) <o eY p) ->
  ( CNFb_axo eZ cZ (succ p +c m) /\
    CNFbvo eX cX (n +c m) +o CNFbvo eY cY (succ p) =
      CNFbvo eZ cZ (succ p +c m)).
Proof.
move=> nB mB pB ax ay lt1 plt2.
have spB:= (BS_succ pB).
move:(CNFb_A nB mB ax) => [ax4 ax5 ->].
have axz: CNFb_axo eZ cZ (succ p +c m).
  apply:(CNFb_m_ax spB mB ay ax5);right.
  by rewrite /cnf_s (csum0l (CS_Bnat nB)) (cpred_pr1 (CS_Bnat pB)).
split; first by exact.
have oY:=(OS_CNFb spB ay).
rewrite - (osumA (OS_CNFb mB ax5) (OS_CNFb nB ax4) oY).
move:(CNFb_A spB mB axz) => [au av ->].
apply f_equal2.
  apply:(osum_expansion_exten mB) => i lim.
  have ww: ~ (i +c succ p <c succ p).
    rewrite csumC=> h; move:(csum_M0le i (CS_succ p)) => h'; co_tac.
  rewrite /cantor_mon /eZ/cZ /cnf_m /cnf_s; Ytac0; Ytac0.
  by rewrite (cdiff_pr1 (BS_lt_int lim mB) spB).
have <-: CNFbvo eY cY (succ p) = CNFbvo eZ cZ (succ p).
  apply:(osum_expansion_exten spB) => i lim.
  by rewrite /eZ/cZ/cantor_mon /cnf_s /cnf_m; Ytac0; Ytac0.
case (equal_or_not n \0c) => nz; first by rewrite nz CNFq_p2 osum0l //.
move: ax4; move: (cpred_pr nB nz)=> [n'B ->] ax4.
by apply: (ord_negl_p8 ax4 ay n'B pB); case:plt2.
Qed.

Lemma CNF_sum_pr2 eX cX eY cY n m p
  (eZ := cnf_m eY (cnf_s eX (succ n)) (succ p))
  (cZ := cnf_m (cnf_change_n cY p ((cX n) +o (cY p)))
         (cnf_s cX (succ n)) (succ p))
  (Z:= CNFbB (succ p +c m) eZ cZ):
  inc n Bnat -> inc m Bnat -> inc p Bnat ->
  CNFb_axo eX cX (succ n +c m) ->
  CNFb_axo eY cY (succ p) ->
  eY p = eX n ->
  ( CNFb_axo eZ cZ (succ p +c m) /\
    CNFbvo eX cX ((succ n) +c m) +o CNFbvo eY cY (succ p) =
    CNFbvo eZ cZ (succ p +c m)).
Proof.
move=> nB mB pB ax ay eq1.
have nmB:= BS_sum nB mB.
have spB := BS_succ pB.
have snB:= (BS_succ nB).
have cdx1: succ n +c m = n +c succ m.
   by rewrite csum_via_succ // csum_via_succ1.
have nls: n <c succ n +c m.
  by rewrite cdx1;apply:(finite_sum4_lt nB);apply: succ_nz.
have pa: cX n <o omega0 by move:(ax) => [[_ _ ha _ ] _]; apply: ha.
move: (CNFb_change_nv pB ay pa)=> []; rewrite -/cY -/eY => ax4 v4.
move:(CNFb_A snB mB ax) => [ax5 ax6 ->].
move: (ax) => [[_ _ _ h2] _].
have axz: CNFb_axo eZ cZ (succ p +c m).
  apply:(CNFb_m_ax spB mB ax4 ax6).
  rewrite (cpred_pr1 (CS_Bnat pB)) /cnf_s (csum0l (CS_Bnat snB)).
  rewrite -/eY eq1; case (equal_or_not m \0c)=> mz; first by left.
  right; apply: (h2 _ nB); by apply:finite_sum4_lt.
split; first by exact.
have oY:= (OS_CNFb spB ay).
move: (CNFb_A spB mB axz) => [ax7 ax8 ->].
rewrite- (osumA (OS_CNFb mB ax6) (OS_CNFb snB ax5) oY); apply: f_equal2.
  apply: (osum_expansion_exten mB) => i im.
  have ww: ~ (i +c succ p <c succ p).
    rewrite csumC=> h; move:(csum_M0le i (CS_succ p)) => h'; co_tac.
  rewrite /cantor_mon /eZ/cZ /cnf_m /cnf_s; Ytac0; Ytac0.
  by rewrite (cdiff_pr1 (BS_lt_int im mB) spB).
have ->: CNFbvo eZ cZ (succ p) =
   omega0 ^o eY p *o cX n +o CNFbvo eY cY (succ p).
   rewrite - v4; apply: (osum_expansion_exten spB) => i im.
   by rewrite /cantor_mon /eZ /cZ /cnf_s /cnf_m; Ytac0; Ytac0.
have h1 :=(CNFq_p0 (proj1 ax5) (card_lt_succ nB)).
have ax9:= (CNFb_p5 nB ax5).
rewrite (CNFq_p1 _ _ _ nB) eq1 - (osumA h1 (OS_CNFb nB ax9) oY).
congr (ord_sum2).
case (equal_or_not n \0c) => nz.
  rewrite nz CNFq_p2 (osum0l oY); reflexivity.
move: ax9; move: (cpred_pr nB nz)=> [n'B eq]; rewrite eq => ax9.
apply: (ord_negl_p8 ax9 ay n'B pB).
by rewrite -/eY eq1; rewrite {2} eq; apply: h2 => //; rewrite - eq //.
Qed.

Lemma CNF_sum_pr3 eX cX eY cY n p
  (eZ := cnf_m eY (cnf_s eX (succ n)) (succ p))
  (cZ := (cnf_change_n cY p ((cX n) +o (cY p)))):
  inc n Bnat -> inc p Bnat ->
  CNFb_axo eX cX (succ n) ->
  CNFb_axo eY cY (succ p) ->
  eY p = eX n ->
  ( CNFb_axo eZ cZ (succ p) /\
    CNFbvo eX cX (succ n) +o CNFbvo eY cY (succ p) =
    CNFbvo eZ cZ (succ p)).
Proof.
move => nB pB ax ay h1.
have aux1: (succ p +c \0c) = succ p by aw; rewrite /succ; fprops.
have aux2: (succ n +c \0c) = succ n by aw; rewrite /succ; fprops.
rewrite - aux2 in ax.
move:(CNF_sum_pr2 nB BS0 pB ax ay h1); rewrite aux1 aux2 -/eZ.
set c2 := cnf_m _ _ _.
have pa: same_below eZ eZ (succ p) by [].
have pb: same_below c2 cZ (succ p).
  by move => i idn;rewrite /c2 /cZ /cnf_m/cnf_change_n; Ytac0.
move =>[sa ->]; exact: (CNFb_exten pa pb sa (BS_succ pB)).
Qed.

Lemma CNF_prod_pr1 eX cX p k
  (cZ := (cnf_change_n cX p ((cX p) *o k))):
  \0o <o k -> k <o omega0 ->
  inc p Bnat -> CNFb_axo eX cX (succ p) ->
  (CNFb_axo eX cZ (succ p) /\
    CNFbvo eX cX (succ p) *o k= CNFbvo eX cZ (succ p)).
Proof.
move => kz ko pB ax.
pose ck k := cnf_change_n cX p (cX p *o k).
rewrite /cZ -/(ck k).
have psp:= card_lt_succ pB.
have pa: \0o <o cX p by move: (ax) => [_]; apply.
have H: forall k, \0o <o k -> k <o omega0 -> CNFb_axo eX (ck k) (succ p).
  move => n na nb; apply:CNF_change_n_ax => //; first by apply: oprod2_pos.
  apply: oprod2_lt_omega => //.
  move: (ax) => [[_ _ a2 _] _]; exact:(a2 _ psp).
move: (H _ kz ko) => az; split; first by exact.
pose r k := CNFbvo eX cX (succ p) *o k = CNFbvo eX (ck k) (succ p).
have spB:= (BS_succ pB).
have oX:=(OS_CNFb spB ax).
have r1: r \1c.
  rewrite /r (oprod1r oX) ; apply: (osum_expansion_exten spB).
  move => i isp; rewrite /ck /cnf_change_n /cantor_mon; Ytac h=> //.
  rewrite h oprod1r //; ord_tac.
have kB: inc k Bnat by apply /olt_omegaP.
have k1: \1c <=c k.
  by apply: card_ge1; fprops => ekz; move: (proj2 kz); rewrite ekz.
apply: (cardinal_c_induction2 (r :=r) BS1 r1 _ kB k1).
move => n nB n1 rn.
have on:= Bnat_oset nB.
have ->: succ n = succ_o n by apply: succ_of_Bnat.
rewrite /r /ck (oprod2_succ oX on) rn.
have n0: \0o <o n.
  apply: ord_ne0_pos => // nz.
  move: card_lt_01 => h1; rewrite nz in n1; co_tac.
have no: n <o omega0 by apply/ olt_omegaP.
have ax2:=(H _ n0 no).
move: (CNF_sum_pr3 pB pB ax2 ax (erefl (eX p))) => [ax3 ->].
apply: (osum_expansion_exten spB) => i isb.
have->: ck n p +o cX p = cX p *o succ_o n.
   rewrite /ck /cnf_change_n; Ytac0; rewrite (oprod2_succ _ on) //; ord_tac.
by rewrite /cantor_mon /cnf_m; Ytac0.
Qed.

Lemma CNF_limit e c n:
  inc n Bnat -> CNFb_axo e c (succ n) -> \0o <o (e \0c) ->
  exists2 z, ordinalp z & CNFbvo e c (succ n) = omega0 *o z.
Proof.
move => nB ax h0.
have h1: forall k, inc k Bnat -> CNFb_axo e c (succ k) ->
  exists2 z, ordinalp z & cantor_mon omega0 e c k = omega0 *o z.
  move => k kB axk; rewrite /cantor_mon.
  have kp: \0c <=c k by fprops.
  move: (CNF_exponents_M (BS_succ kB) (proj1 axk) kp (card_lt_succ kB)) => ek.
  move: (ord_rev_pred (ord_lt_leT h0 ek)) => [op ->].
  have oo:= OS_omega.
  have ob:= OS_pow oo op.
  move: (proj2 axk _ (card_lt_succ kB)) => [[_ oc _] _].
  rewrite (opow_sum oo OS1 op) (opowx1 oo) - (oprodA oo ob oc).
  exists (omega0 ^o (e k -o \1o) *o c k); [ exact: (OS_prod2 ob oc) | done].
move: n nB ax; apply: cardinal_c_induction.
  move => ha; move: (h1 _ BS0 ha) =>[z z1 z2]; exists z => //.
  move: (proj1 ha); rewrite succ_zero /CNFbvo => hb.
  by rewrite (CNFq_p3 hb card_lt_01).
move => n nB Hrec ax.
rewrite /CNFbvo (CNFq_p1 _ _ _ (BS_succ nB)) -/CNFbvo.
move: (Hrec (CNFb_p5 (BS_succ nB) ax)) => [z1 oz1 ->].
move:(h1 _ (BS_succ nB) ax) => [z2 oz2 ->].
rewrite - (osum_prodD oz2 oz1 OS_omega); exists (z2 +o z1); fprops.
Qed.

Lemma CNF_limit0 e c n:
  inc n Bnat -> CNFb_axo e c (succ n) ->
  (exists2 y, ordinalp y & CNFbvo e c (succ n) = y +o omega0 ^o (e \0c)).
Proof.
move => nB ax.
move: (CNFb_Al nB ax) => [sa sb ->].
have osn:= (succ_positive n).
move: (proj2 ax _ (succ_positive n)) => cp.
move: (ax) => [[_ h1 h2 _] _].
have o1:= (OS_pow OS_omega (h1 _ osn)).
have o2:= (OS_diff OS1 (proj32_1 cp)).
have o3 := (OS_prod2 o1 o2).
move: (oprec_nat (proj2 ax _ (succ_positive n)) (h2 _ osn)) => eq1.
rewrite /cantor_mon eq1 (oprod2_succ o1 o2).
rewrite (osumA (OS_CNFb nB sb) o3 o1).
set y := (CNFbv _ _ _ _ +o _); exists y => //.
apply: (OS_sum2 (OS_CNFb nB sb) o3).
Qed.

Lemma CNF_limit1 e c n:
  inc n Bnat -> CNFb_axo e c (succ n) ->
  (e \0c) = \0o -> ~ (limit_ordinal (CNFbvo e c (succ n))).
Proof.
move => nb ax; move: (CNF_limit0 nb ax) => [z zv ->] ->.
rewrite opowx0 (ord_succ_pr zv) => lz.
move /(limit_ordinal_P1 (OS_succ zv)): lz => [ _ h].
by apply:(proj2 (osuccVidpred (OS_succ zv))); split => //; exists z.
Qed.

Lemma CNF_limit2 e c n:
  inc n Bnat -> CNFb_axo e c (succ n) ->
  \0o <o (e \0c) -> (limit_ordinal (CNFbvo e c (succ n))).
Proof.
move => nb ax h1.
move: (CNF_limit0 nb ax) => [z oz ->].
apply: osum_limit => //;apply: indecomp_limit2 => //.
Qed.

Lemma CNF_limit3 x: limit_ordinal x ->
  exists2 z, ordinalp z & x = omega0 *o z.
Proof.
move =>lx.
move: (the_CNF_p0 (proj31 lx)) => [ax xv].
move: (CNFB_ax_simp ax) => [sa sb].
move:(the_CNF_p2 (limit_positive lx)); set m := cpred _.
move => [mB pv]; rewrite pv in sb.
move:(sb)=> [[_ ha _ _] _].
move: (ha _ (succ_positive m)) (CNF_limit1 mB sb).
rewrite - pv -/(CNFBv _) xv => hb hc.
have sc: \0o <o Vg (P (Q (the_CNF x))) \0c.
  by apply: ord_ne0_pos => // nz; case: (hc nz).
by move: (CNF_limit mB sb sc);rewrite - pv -/(CNFBv _) xv.
Qed.

Lemma CNF_se_p e c e0 n (eY:= fun i => e0 +o e i):
   inc n Bnat -> CNFb_axo e c n -> ordinalp e0 ->
   (CNFb_axo eY c n /\
   CNFbvo eY c n = omega0 ^o e0 *o CNFbvo e c n).
Proof.
move => nB ax oe.
have aux: forall k, CNFb_axo e c k -> CNFb_axo eY c k.
 move => k [[pa pb pc pd] pe]; split; [split |] => //.
 + move => i /pb oei; rewrite /eY; fprops.
 + move => i iB /(pd _ iB) sa; rewrite /eY; apply: (osum_Meqlt sa oe).
   split; first by apply: aux.
rewrite /CNFbvo.
move:n nB ax;apply: cardinal_c_induction.
 by move => _; rewrite !CNFq_p2 oprod0r.
move => n nB Hr ax.
move: (CNFb_p5 nB ax) => ax1.
have oa:=(CNFq_p0 (proj1 ax) (card_lt_succ nB)).
have ob:= (OS_CNFb nB ax1).
have oc:= OS_pow OS_omega oe.
have od:= (CNFb_p8 ax nB).
move: ax => [[_ _ hb _] _].
move: (hb _ (card_lt_succ nB)) => [[og _ _] _].
rewrite (CNFq_p1 _ _ _ nB) (CNFq_p1 _ _ _ nB) (Hr ax1) (osum_prodD oa ob oc).
rewrite /eY /cantor_mon (opow_sum OS_omega oe od).
by rewrite (oprodA oc (OS_pow OS_omega od) og).
Qed.

Lemma CNF_prod_pr2 e c n:
  inc n Bnat -> CNFb_axo e c (succ n) ->
  CNFbvo e c (succ n) *o omega0 = omega0 ^o (e n) *o omega0.
Proof.
move=> nB ax; rewrite /CNFbvo.
have nn:= card_lt_succ nB.
move: (ax) => [[_ h1 h2 _] h3].
have h4:= (OS_pow OS_omega (h1 _ nn)).
have h5:= OS_prod2 h4 OS_omega.
have h3n:= (h3 _ nn).
apply: ord_leA; last first.
  have ox:= (OS_CNFb nB (CNFb_p5 nB ax)).
  apply: (oprod_Mleeq _ OS_omega); apply: (ord_leT (oprod_Mle1 h4 h3n)).
  rewrite (CNFq_p1 _ _ _ nB); apply: (osum_Mle0 (CNFq_p0 (proj1 ax) nn) ox).
set x := CNFbvo e c (succ n).
have pc' : \0o <o x by apply:(CNFq_pg5 nB ax).
have ox: ordinalp x by ord_tac.
move: (oprod_normal pc') => [pg pk].
rewrite (pk _ (omega_limit)); clear pk.
apply: ord_ub_sup => //.
  move=> t /funI_P [z zo ->]; apply: OS_prod2 => //.
  by apply: (ordinal_hi OS_omega zo).
move=> i /funI_P [z zo ->].
move: zo => /(ord_ltP OS_omega) zo.
have oz: ordinalp z by ord_tac.
case: (ord_zero_dichot oz) => zno.
  by rewrite zno (oprod0r); apply: ozero_least.
move: (oprod2_lt_omega (h2 _ nn) zo) => /ord_succ_ltP so.
move: (CNF_prod_pr1 zno zo nB ax) => [ax1 ->].
rewrite /CNFbvo (CNFq_p1 _ _ _ nB).
have lt1: CNFbvo e (cnf_change_n c n (c n *o z)) n <o omega0 ^o (e n).
  apply:(CNFq_pg3 nB (CNFq_p5 nB (proj1 ax1)) (h1 _ nn)).
    move => k kn; exact:(CNF_exponents_sM (BS_succ nB) (proj1 ax) kn nn).
rewrite {1} /cnf_change_n /cantor_mon; Ytac0.
have oy1:= (OS_prod2 (proj32_1 h3n) oz).
move: (osum_Meqlt lt1 (OS_prod2 h4 oy1)); rewrite - (oprod2_succ h4 oy1).
move => [le1 _]; apply: (ord_leT le1 (oprod_Meqle so h4)).
Qed.

Lemma CNF_prod_pr2bis x: \0o <o x->
  x *o omega0 = omega0 ^o (the_CNF_degree x) *o omega0.
Proof.
move => xp.
move: (the_CNF_p0 (proj32_1 xp)) => [ax0 xv].
move: (CNFB_ax_simp ax0) (the_CNF_p2 xp) => [pb ax] [nB pc].
by rewrite pc in ax; rewrite - (CNF_prod_pr2 nB ax) - {1} xv - pc.
Qed.

Lemma oprod_crit_aux n x (y := omega0 ^o (omega0 ^o n)):
  ordinalp n -> \1o <=o x -> x <o y -> x *o y = y.
Proof.
move => on x1 xy.
move: OS_omega OS1 ord_lt_1omega => oo o1 lt1o.
have oy1:= OS_pow OS_omega on.
have oy:= OS_pow OS_omega oy1.
have xp: \0o <o x by ord_tac1.
have yif: omega0 <=o y.
   rewrite /y - {1} (opowx1 OS_omega); apply: opow_Momega_le.
   by apply: ord_ge1 => //; apply: omega_pow_nz.
case: (ord_zero_dichot on) => nz.
  move: xy;rewrite /y nz opowx0 (opowx1 OS_omega) => aux2.
  rewrite oprod_int_omega //.
have aux1: omega0 *o (omega0 ^o (omega0 ^o n)) = omega0 ^o (omega0 ^o n).
  rewrite - {1} (opowx1 OS_omega) - opow_sum //;congr (_ ^o _).
  apply: ord_negl_p2 => //.
have ox: ordinalp x by ord_tac.
have od:=(OS_the_CNF_degree ox).
rewrite /y - aux1 (oprodA ox oo oy) (CNF_prod_pr2bis xp).
rewrite - (oprodA (OS_pow oo od) oo oy) aux1.
move: xy; rewrite - (opow_sum oo od oy1) => h2.
by rewrite (indecomp_prop1 (indecomp_prop4 on) (the_CNF_p6 oy1 xp h2)).
Qed.

Lemma CNF_prod_pr3 x y: \0o <o x-> limit_ordinal y ->
  x *o y = omega0 ^o (the_CNF_degree x) *o y.
Proof.
move=> xnz ly; move:(proj32_1 xnz) => ox.
move: (CNF_limit3 ly) => [z oz yv].
have ot := (OS_pow OS_omega (OS_the_CNF_degree ox)).
by rewrite yv (oprodA ox OS_omega oz) (CNF_prod_pr2bis xnz)
 (oprodA ot OS_omega oz).
Qed.

Lemma CNF_prod_pr4 eX cX n eY cY p
  (x:= CNFbvo eX cX (succ n)) (y:= CNFbvo eY cY (succ p)):
  inc n Bnat -> inc p Bnat ->
  CNFb_axo eX cX (succ n) -> CNFb_axo eY cY (succ p) ->
  \0o <o eY \0c -> x *o y = omega0 ^o (eX n) *o y.
Proof.
move=> nB pB ax ay pnz.
move: (CNF_limit2 pB ay pnz) => ly.
move: (the_CNF_p5 nB ax) => [xp dx].
by move:(CNF_prod_pr3 xp ly); rewrite -/y dx.
Qed.

Lemma CNF_prod_pr5 eX cX n eY cY p
      (eZ:= fun i => (eX n) +o eY i)
      (x:= CNFbvo eX cX (succ n)) (y:= CNFbvo eY cY (succ p)):
  inc n Bnat -> inc p Bnat ->
  CNFb_axo eX cX (succ n) -> CNFb_axo eY cY (succ p) ->
  \0o <o eY \0c ->
  (CNFb_axo eZ cY (succ p) /\ x *o y = CNFbvo eZ cY (succ p)).
Proof.
move=> nB pB ax ay pnz.
move: (CNF_se_p (BS_succ pB) ay (CNFb_p8 ax nB)) => [sa ->]; split => //.
by rewrite CNF_prod_pr4.
Qed.

Lemma CNF_prod_pr6 eX cX n eY cY p
  (x:= CNFbvo eX cX (succ n)) (y:= CNFbvo eY cY (succ p))
  (cZ := cnf_m (cnf_change_n cX n (cX n *o cY \0o))
      (fun z => cY (succ z)) (succ n))
  (eZ := cnf_m eX (fun z => (eX n) +o (eY (succ z))) (succ n)):
  inc n Bnat -> inc p Bnat ->
  CNFb_axo eX cX (succ n) -> CNFb_axo eY cY (succ p) ->
   eY \0c = \0o ->
  (CNFb_axo eZ cZ (succ (n +c p)) /\ x *o y = CNFbvo eZ cZ (succ (n +c p))).
Proof.
move => nB pB ax ay tcy.
rewrite /y.
have spp:= (succ_positive p).
move: (proj2 ay _ spp) => c0p.
have snB:= (BS_succ nB).
have oc0: ordinalp (cY \0c) by ord_tac.
have c0o: cY \0c <o omega0 by move:(ay)=> [[_ _ h _] _]; apply: (h _ spp).
have [om1 ay1 ->] := (CNFb_Al pB ay).
move: (OS_CNFb pB ay1) (OS_CNFb snB ax) => oy1 ox.
rewrite (osum_prodD oy1 om1 ox) /cantor_mon tcy opowx0 (oprod1l oc0).
move: (CNF_prod_pr1 c0p c0o nB ax) => [ax1 ->].
move: ax1; rewrite /cZ; set c1:= (cnf_change_n cX n (cX n *o cY \0c)) => ax1.
case (equal_or_not p \0o) => pnz.
  rewrite pnz (csum0r (CS_Bnat nB)) CNFq_p2 oprod0r (osum0l (OS_CNFb snB ax1)).
  apply:(CNFb_exten _ _ ax1 snB).
    by move => i lin; rewrite /eZ/cnf_m; Ytac0.
  by move => i lin; rewrite /cnf_m; Ytac0.
move: (cpred_pr pB pnz); set m := (cpred p); move => [mB mv].
rewrite mv in ay1; rewrite mv.
have cp1: (\0o <o eY (succ \0c)).
  have c1p: succ \0o <c succ p.
    apply/(card_succ_succ_ltP BS0 pB); rewrite mv; apply: succ_positive.
  have lt01:= succ_positive \0c.
  move:(CNF_exponents_sM (BS_succ pB) (proj1 ay) lt01 c1p) => h.
  apply: (ord_ne0_pos (proj32_1 h) (ord_gt_ne0 h)).
move:(CNF_prod_pr5 nB mB ax ay1 cp1) => [ax2 ->].
have sa: \0c +c succ m = succ m by rewrite csum0l; fprops.
have sb: eX n <o eX n +o eY (succ \0c).
  have h2:= CNFb_p8 ax nB.
  by move: (osum_Meqlt cp1 h2); rewrite (osum0r h2).
have sc: \0c = \0c \/ eX n +o eY (succ (cpred \0c)) <o eX n by left.
rewrite - sa in ax2 |- *.
move: (CNF_sum_pr1 BS0 (BS_succ mB) nB ax2 ax1 sb sc) => [ax3 ->].
rewrite sa - (csum_via_succ1 (succ m) nB).
have hh: forall k, (k -c (succ n)) +c \0c = (k -c (succ n)).
  move=> k; exact:(csum0r (CS_cardinal (k -s succ n))).
apply: (CNFb_exten _ _ ax3 (BS_sum (BS_succ nB) (BS_succ mB))).
  by move => i isn; rewrite/eZ /cnf_m /cnf_s hh.
by move => i isn; rewrite /cnf_m /cnf_s hh.
Qed.

Lemma CNF_succ_pow1 n c (x := (succ_o ((omega0 ^o n) *o c)))
   (X:= the_CNF x) (eX := (Vg (P (Q (the_CNF x)))))
   (cX := (Vg (Q (Q (the_CNF x))))):
  \0o <o n -> \0o <o c -> c <o omega0 ->
  [/\ CNFb_axo eX cX (P X), CNFbvo eX cX (P X) = x &
   [/\ P X = \2c, eX \0c = \0o, eX \1c = n, cX \0c = \1o & cX \1c = c]].
Proof.
move: OS0 => oz np cp lco.
move: ord_le_2omega BS2 BS1 => le2 bs2 bs1.
have oc: ordinalp c by ord_tac.
have on: ordinalp n by ord_tac.
have o1:= OS_pow OS_omega on.
have o2:= OS_prod2 o1 oc.
move: BS0 card_lt_02 card_lt_12 card_lt_01 => bs0 lt02 lt12 [_ n01].
set ex := fun z => Yo (z = \0c) \0o n.
set cx := fun z => Yo (z = \0c) \1o c.
have e0: ex \0c = \0o by rewrite /ex; Ytac0.
have e1: ex \1c = n by rewrite /ex; Ytac0.
have c0: cx \0c = \1o by rewrite /cx; Ytac0.
have c1: cx \1c = c by rewrite /cx; Ytac0.
have ax: CNFb_axo ex cx \2c.
  split; first split.
  + exact.
  + by move => i lin; rewrite /ex; Ytac h.
  + by move => i lin; rewrite /cx; Ytac h => //; apply: ord_lt_1omega.
  + move => i iB /card_lt2; rewrite /ex; move: (@succ_nz i) => ss.
    rewrite - succ_zero; case => // si.
    by move: (succ_injective1 (CS_Bnat iB) CS0 si) => iz; Ytac0; Ytac0.
  + by move => i lin; rewrite /cx; Ytac h => //; apply: ord_lt_01.
move: (CNFbB_prop ax BS2); set y := (CNFbB \2c ex cx); move => [ax2 yv].
have py2: P y = \2c by rewrite /y /CNFbB; aw.
move: (the_CNF_p0 (OS_succ o2)); rewrite -/x; move => [ax3 xv].
move: (CNFB_ax_simp ax2) => [_ ax4].
have eq1:CNFBv (the_CNF x) = CNFBv y.
  rewrite py2 in ax4.
  move: (CNFq_p7 (proj1 ax4) lt12).
  rewrite xv /CNFBv /y /CNFbB !(pr1_pair, pr2_pair) -/CNFbvo => ->.
  rewrite /cantor_mon; bw; repeat Ytac0; rewrite e0 c0 e1 c1.
  by rewrite opowx0 (oprod1r OS1) (ord_succ_pr o2).
move:(CNFB_unique ax3 ax2 eq1).
rewrite -/(CNFBv (the_CNF x)) xv /eX /cX -/X => ->; split.
+ exact.
+ exact.
+ by rewrite /y /CNFbB !(pr1_pair, pr2_pair); bw; repeat Ytac0; split.
Qed.

Lemma CNF_succ_pow n (x := (succ_o (omega0 ^o n)))
   (X:= the_CNF x) (eX := (Vg (P (Q (the_CNF x)))))
   (cX := (Vg (Q (Q (the_CNF x))))):
  \0o <o n ->
  [/\ CNFb_axo eX cX (P X), CNFbvo eX cX (P X) = x &
  [/\ P X = \2c, eX \0c = \0o, eX \1c = n, cX \0c = \1o & cX \1c = \1o]].
Proof.
move: ord_lt_01 ord_lt_1omega => hb hc ha.
move:(CNF_succ_pow1 ha hb hc).
by rewrite(oprod1r (OS_pow OS_omega (proj32_1 ha))).
Qed.

Lemma succ_pow_omega_irred p a b:
  ordinalp p -> ordinalp a -> ordinalp b ->
  succ_o (omega0 ^o p) = a *o b ->
  (a = \1o \/ b = \1o).
Proof.
move=>on oa ob sab.
case: (ord_zero_dichot on) => pz.
  by apply: oprod2_two => //; rewrite - sab pz opowx0 // succ_o_one.
move: (CNF_succ_pow pz).
set ep := Vg _ ; set cp := Vg _; set np:= (P (the_CNF _)).
move => [axp pv etc].
have snz: a *o b <> \0o by rewrite - sab;apply: succo_nz.
have anz: a <> \0o by move =>h; case: snz; rewrite h oprod0l.
have bnz: b <> \0o by move =>h; case: snz; rewrite h oprod0r.
have ap: \0o <o a by ord_tac1.
have bp: \0o <o b by ord_tac1.
move: (the_CNF_p0 oa) (the_CNF_p0 ob) => [sa sb][sc sd].
move: (CNFB_ax_simp sa) (CNFB_ax_simp sc) (the_CNF_p2 ap) (the_CNF_p2 bp).
move: sb sd; rewrite /CNFBv.
set ea := Vg _ ; set ca := Vg _; set na:= (P (the_CNF a)); set ma:=(cpred na).
set eb := Vg _ ; set cb := Vg _; set nb:= (P (the_CNF b)); set mb:=(cpred nb).
move => va vb [naB axa] [nbB axb] [maB mav] [mbB mbv].
rewrite mav in axa; rewrite mbv in axb.
move: (succ_positive ma)(succ_positive mb) => spa spb.
move: (axb)(axa) => [[_ h1b h2b _] _] [[_ h1a h2a _] _].
have oeb0:= (h1b _ spb).
have ocb0:= (proj31_1 (h2b _ spb)).
have oca0:= (proj31_1 (h2a _ spa)).
move: etc => [x0 x1 x2 x3 x4].
have npB: inc np Bnat by rewrite x0; fprops.
case (ord_zero_dichot oeb0) => ez.
  move: (CNF_prod_pr6 maB mbB axa axb ez).
  rewrite -mav -mbv va vb; move => [su sv].
  move: pv; rewrite sab sv => eq.
  have dB:= (BS_succ (BS_sum maB mbB)).
  move: (CNFb_unique npB dB axp su eq); rewrite x0; move => [ha hb].
  have nap: \0c <c na by rewrite mav; apply: succ_positive.
  have mab1: \1c = (ma +c mb).
    by apply: (succ_injective1 CS1 (CS_sum2 ma mb)); rewrite succ_one.
  move:(hb _ card_lt_02) (hb _ card_lt_12);rewrite x1 x2 x3 x4.
  rewrite /cnf_m /cnf_change_n; Ytac0; Ytac0.
  case (equal_or_not ma \0c) => maz.
    rewrite mav maz succ_zero; Ytac0; move => [ua ub].
    symmetry in ub; move: (oprod2_one oca0 ocb0 ub) => [ca0 _] _.
    left; rewrite - va mav maz succ_zero.
    rewrite /CNFbvo (CNFq_p3 (proj1 axa) spa).
    by rewrite /cantor_mon -ua ca0 opowx0 (oprod1r OS1).
  move: card_lt_12 => l12.
  case (equal_or_not mb \0c) => mbz _ [ _] .
    move:mab1; rewrite mbz (csum0r (CS_Bnat maB)) => ma1.
    rewrite mav - ma1 succ_one; Ytac0; Ytac0 => eq1.
    have hh: \1c <c succ ma by rewrite - ma1; apply: (card_lt_succ BS1).
    have oca1: ordinalp (ca \1c) by move: (h2a _ hh) => hh1; ord_tac.
    symmetry in eq1; move: (oprod2_one oca1 ocb0 eq1) => [_ cb0].
    right; rewrite - vb mbv mbz succ_zero.
    rewrite /CNFbvo (CNFq_p3 (proj1 axb) spb).
    by rewrite /cantor_mon ez cb0 opowx0 (oprod1r OS1).
  move: (card_ge1 (CS_Bnat maB) maz) (card_ge1 (CS_Bnat mbB) mbz).
  move => l1 l2; move: (csum_Mlele l1 l2); rewrite - mab1 card_two_pr.
  move => l12'; co_tac.
move: (CNF_prod_pr5 maB mbB axa axb ez).
rewrite -mav -mbv va vb; move => [su sv].
move: pv; rewrite sab sv => eq.
move: (CNFb_unique npB nbB axp su eq); rewrite x0; move => [ha hb].
move:(esym (proj1 (hb _ card_lt_02))); rewrite x1 => e1.
move:(osum2_zero (h1a _ (card_lt_succ maB)) oeb0 e1) => [_ h].
by move: (proj2 ez); rewrite h; case.
Qed.

Lemma opow_int_omega n:
  \2o <=o n -> n <o omega0 -> n ^o omega0 = omega0.
Proof.
move=> n2 no.
have oo:= OS_omega.
have on: ordinalp n by ord_tac.
have nz: \0o <o n by move: (ord_lt_leT ord_lt_02 n2).
apply:ord_leA.
  rewrite (proj2 (opow_normal n2) _ omega_limit).
  apply: ord_ub_sup => //.
    move=> v => /funI_P [z /(ordinal_hi oo) zy ->]; fprops.
  by move => x /funI_P [z /(ord_ltP oo) /(opow_2int1 no) [zo _] ->].
by move: (opow_Mspec2 oo n2); rewrite oprod_int_omega.
Qed.

Lemma CNF_pow_pr1 e c n k (x:= CNFbvo e c (succ n)):
  CNFb_axo e c (succ n) -> \0o <o e \0c ->
  inc k Bnat -> inc n Bnat ->
  x ^o (succ_o k) = (omega0 ^o ((e n) *o k)) *o x.
Proof.
move=> ax ep kB nB.
have ox:=(OS_CNFb (BS_succ nB) ax).
have olcx:= CNFb_p8 ax nB.
move: k kB; apply: cardinal_c_induction.
  by rewrite (oprod0r) opowx0 (oprod1l ox) succ_o_zero opowx1.
move=> k kB hrec.
move/BnatP: (kB) => p2.
have ok:= proj1 p2.
have osk:= OS_succ ok.
have odf:=(OS_prod2 olcx ok).
move: (OS_pow OS_omega odf)(OS_pow OS_omega olcx) => of1 ood.
rewrite (succ_of_finite p2) (opow_succ ox osk) hrec - (oprodA of1 ox ox).
rewrite (CNF_prod_pr4 nB nB ax ax ep) (oprodA of1 ood ox)(oprod2_succ olcx ok).
by rewrite - (opow_sum OS_omega odf olcx).
Qed.

Lemma CNF_pow_pr2 ex cx ey cy n m k:
  CNFb_axo ex cx (succ n) -> CNFb_axo ey cy m ->
  inc n Bnat -> inc m Bnat -> inc k Bnat -> k <> \0c -> \0o <o ex n ->
  (CNFbvo ex cx (succ n)) ^o k = (CNFbvo ey cy m) ->
  [/\ m <> \0c, ey (cpred m) = (ex n) *o k & cy (cpred m) = cx n].
Proof.
move => ax ay nB mB kB kp cnp.
move: (cpred_pr kB kp) => [pa pb].
have snB:= (BS_succ nB).
set q := cpred k in pa pb; rewrite pb.
clear kB kp pb; move: q pa m ey cy mB ay; clear k.
have ox:= OS_CNFb snB ax.
have lts:=(card_lt_succ nB).
have oo: ordinalp (ex \0o).
  move: (ax) =>[[_ h1 _ _] _]; exact: (h1 _ (succ_positive n)).
have op:= CNFb_p8 ax nB.
apply: cardinal_c_induction.
  move=> m ey cy mB ay.
  rewrite succ_zero (opowx1 ox) (oprod1r op) => eq1.
  move: (CNFb_unique snB mB ax ay eq1) => [<- hb].
  rewrite (cpred_pr2 nB); move:(hb _ lts) => [-> ->].
  split; [apply: succ_nz | exact | exact].
move => k kB Hrec m ey cy mB ay.
have skB:= (BS_succ kB).
move/BnatP: (skB) => p2.
have ok:= proj1 p2.
have osk:= OS_succ ok.
rewrite (succ_of_finite p2) (opow_succ ox ok).
move: (the_CNF_p0(OS_pow ox ok)) => [/CNFB_ax_simp]; rewrite /CNFBv.
set w :=_ ^o _; set ew := Vg _; set cw := Vg _; set m' := P _.
move => [m'B ay'] yv eq1.
move: (Hrec _ _ _ m'B ay' (esym yv)) => [ha hb hc].
move: (cpred_pr m'B ha)=> [m''B mv].
rewrite mv in ay'.
case (ord_zero_dichot oo) => h0.
  move: (CNF_prod_pr6 m''B nB ay' ax h0) => [ax2].
  rewrite - mv yv eq1 => eq2.
  have nmB:= (BS_sum m''B nB).
  rewrite {2 5} mv in eq2.
  move: (CNFb_unique mB (BS_succ nmB) ay ax2 eq2) => [-> h].
  rewrite (cpred_pr2 nmB); move:(h _ (card_lt_succ nmB)) => [-> ->].
  case:(equal_or_not n \0c) => nez.
    by move: (proj2 cnp); rewrite nez h0; case.
  rewrite /cnf_m /cnf_change_n.
  move: (cpred_pr nB nez) => [hu hv].
  have ->: (cpred m' +c n) = m' +c (cpred n).
    by rewrite {1} hv (csum_via_succ _ hu) - (csum_via_succ1 _ m''B) - mv.
  rewrite - mv; Ytac hw; last Ytac0.
    move:(csum_M0le (cpred n) (CS_Bnat m'B)) => hz; co_tac.
  rewrite (csumC m' (cpred n)) (cdiff_pr1 hu m'B).
  rewrite hb (oprod2_succ op ok) - hv.
  split; [apply: succ_nz | exact | exact].
move: (CNF_prod_pr5 m''B nB ay' ax h0) => [ax2].
rewrite - mv yv eq1 => eq2.
move: (CNFb_unique mB snB ay ax2 eq2) => [-> h].
rewrite (cpred_pr2 nB); move:(h _ (card_lt_succ nB)) => [-> ->].
rewrite hb (oprod2_succ op ok).
split; [apply: succ_nz | exact | exact].
Qed.

Lemma CNF_pow_pr3 e c n:
   CNFb_axo e c (succ n) -> \0o <o (e n) -> inc n Bnat ->
   (CNFbvo e c (succ n)) ^o omega0 = omega0 ^o ((e n) *o omega0).
Proof.
move=> ax exp nB.
have oo:= OS_omega.
have oen:= (proj32_1 exp).
move:(CNFq_pg4 nB ax) (proj1 (CNFq_pg1 nB (proj1 ax))) => le1 le2.
have pa:= (omega_pow_pos oen).
move:(opow_Mleeq (nesym (proj2 pa)) le1 OS_omega).
move:(opow_Mleeq (nesym (proj2 (ord_lt_leT pa le1))) le2 OS_omega).
rewrite - (opow_prod oo (OS_succ oen) oo) - (opow_prod oo oen oo).
rewrite(indecomp_prod3 exp);apply: ord_leA.
Qed.

Lemma CNF_pow_pr4 ex cx n ey cy m
  (x:=CNFbvo ex cx (succ n)) (y:=CNFbvo ey cy (succ m)):
  CNFb_axo ex cx (succ n) -> \0o <o (ex n) -> inc n Bnat ->
  CNFb_axo ey cy (succ m) -> \0o <o (ey \0o) -> inc m Bnat ->
  x ^o y = omega0 ^o ((ex n) *o y).
Proof.
move=> ax xp nB ayn yp mB.
rewrite /y;move:(CNF_limit mB ayn yp) => [z oz ->].
have ox := OS_CNFb (BS_succ nB) ax.
have oo:= OS_omega.
have op := (proj32_1 xp).
rewrite (opow_prod ox oo oz) (CNF_pow_pr3 ax xp nB).
by rewrite - (opow_prod oo (OS_prod2 op oo) oz) (oprodA op oo oz).
Qed.

Lemma CNF_pow_pr5 x ey cy m (y:=CNFbvo ey cy (succ m)):
  \2o <=o x -> x <o omega0 ->
  CNFb_axo ey cy (succ m) -> \0o <o (ey \0o) -> inc m Bnat ->
  exists z,
   [/\ ordinalp z, y = omega0 *o z & x ^o y = omega0 ^o z].
Proof.
move=> x2 xo ayn tcy mB.
have ox: ordinalp x by ord_tac.
rewrite /y;move: (CNF_limit mB ayn tcy) => [z oz ->].
by rewrite (opow_prod ox OS_omega oz) (opow_int_omega x2 xo); exists z.
Qed.

Cantor product form

Definition CNFp_value1 e c := succ_o ((omega0 ^o e) *o c).
Definition CNFp_value2 e c := (succ_o (omega0 ^o e)) *o c.

Lemma CNFp_pr1 e c:
  \0o <o e -> \0o <o c -> c <o omega0 ->
  CNFp_value1 e c = CNFp_value2 e c.
Proof.
move=> ep cp co.
have oe:= (proj32_1 ep).
have oc:= (proj32_1 cp).
have os1:= (OS_pow OS_omega oe).
rewrite /CNFp_value2 /CNFp_value1.
move:(CNF_succ_pow ep) (CNF_succ_pow1 ep cp co).
set ex := Vg _; set cx := Vg _; set n := P _.
set ey := Vg _; set cy := Vg _; set m := P _.
move => [ax <- [n2 ex0 ex1 cx0 cx1]] [ay <- [m2 ey0 ey1 cy0 cy1]].
move: n2; rewrite - succ_one => n2; rewrite n2; rewrite n2 in ax.
move: (CNF_prod_pr1 cp co BS1 ax) => [ax2 ->].
rewrite succ_one m2.
apply: (osum_expansion_exten BS2) => i i2.
rewrite /cantor_mon /cnf_change_n.
case: (card_lt2 i2) => ->.
  by move: card1_nz => h; rewrite ex0 cx0 ey0 cy0; Ytac0.
by rewrite ex1 cx1 ey1 cy1; Ytac0; rewrite (oprod1l oc).
Qed.

Definition CNFp_ax e c n:=
 [/\ (forall i, i <c n -> \0o <o e i),
     (forall i, i <=c n -> \0o <o c i /\ c i <o omega0) &
     ordinalp (e n)].

Definition CNFp_ax1 e c n:=
  (forall i, i <c n -> \0o <o e i) /\
  (forall i, i <c n -> \0o <o c i /\ c i <o omega0).

Definition CNFpv1 e c n :=
  oprod_expansion (fun i => CNFp_value1 (e i) (c i)) n.

Definition CNFpv e c n := ((omega0 ^o (e n)) *o (c n)) *o (CNFpv1 e c n).

Lemma CNFp_ax_ax1 e c n: CNFp_ax e c n -> CNFp_ax1 e c n.
Proof. by move => [a1 a2 _]; split => // i [lin _]; apply a2. Qed.

Lemma OS_CNFp0 e c n: CNFp_ax1 e c n ->
   ord_below_n (fun i => (CNFp_value1 (e i) (c i))) n.
Proof.
move => [ax1 ax2] i lin.
move: (proj32_1 (ax1 _ lin)) (proj32_1 (proj1 (ax2 _ lin))).
move => o1 o2;exact : (OS_succ (OS_prod2 (OS_pow OS_omega o1) o2)).
Qed.

Lemma OS_CNFp1 e c n: CNFp_ax1 e c n -> inc n Bnat -> ordinalp (CNFpv1 e c n).
Proof.
move => ax nB; apply: (OS_oprod_expansion nB) => // i lin.
apply:(OS_CNFp0 ax lin).
Qed.

Lemma OS_CNFp1r e c n m: CNFp_ax1 e c n -> inc n Bnat -> m <=c n ->
   ordinalp (CNFpv1 e c m).
Proof.
move => ax nB nm; apply: (OS_oprod_expansion (BS_le_int nm nB)) => // i lin.
apply:(OS_CNFp0 ax); co_tac.
Qed.

Lemma OS_CNFp e c n: CNFp_ax e c n -> inc n Bnat -> ordinalp (CNFpv e c n).
Proof.
move => ax nB.
move: (OS_CNFp1 (CNFp_ax_ax1 ax) nB) => o1.
move:ax => [_ ax2 ax3].
have oc := (proj32_1(proj1 (ax2 _ (card_leR (CS_Bnat nB))))).
exact (OS_prod2 (OS_prod2 (OS_pow OS_omega ax3) oc) o1).
Qed.

Lemma CNFp_0 e c: CNFpv1 e c \0c = \1o.
Proof. by apply: oprod_expansion0. Qed.

Lemma CNFp_1 e c n: CNFp_ax1 e c n -> \0c <c n ->
  CNFpv1 e c \1c = CNFp_value1 (e \0c) (c \0c).
Proof.
move => ax1 n1; rewrite /CNFpv1 oprod_expansion1 //.
apply: (OS_CNFp0 ax1 n1).
Qed.

Lemma CNFp_A e c n m :
  inc n Bnat -> inc m Bnat -> CNFp_ax1 e c (n +c m) ->
  CNFpv1 e c (n +c m) = (CNFpv1 e c n) *o
    CNFpv1 (fun z => e (z +c n)) (fun z => c (z +c n)) m.
Proof. move => nB mB /OS_CNFp0; exact(oprod_expansionA nB mB). Qed.

Lemma CNFp_r e c n: inc n Bnat ->
  CNFpv1 e c (succ n) = CNFpv1 e c n *o (CNFp_value1 (e n) (c n)).
Proof. move => nB; exact:(oprod_expansion_succ _ nB). Qed.

Lemma CNFp_p2 e c n (a:= e n -o (e (cpred n))) (b := c n):
   CNFb_axo e c (succ n) -> inc n Bnat -> n <> \0c ->
   [/\ \0o <o a, \0o <o b, b <o omega0 &
    CNFbvo e c (succ n) = (CNFbvo e c n) *o (CNFp_value1 a b)].
Proof.
move => ax nB nz.
move: (cpred_pr nB nz) => [pnB pn].
move: (ax) => [[_ _ pe pf] pg].
have nn:= (card_lt_succ nB).
have lt1: e (cpred n) <o e n.
  move: nn; rewrite {1 4} pn => h; exact:(pf _ pnB h).
move: (odiff_pos lt1);move: lt1 => [le1 _]; move: (odiff_pr le1) => [oa pi] pj.
move:(pg _ nn) (pe _ nn) => pa pb.
rewrite -/a in oa.
split => //.
move: (ord_rev_pred pj) =>[]; rewrite -/a => osa av.
have ax2:= (CNFb_p5 nB ax).
have pc:= (OS_CNFb nB ax2); rewrite pn in ax2.
have ocn := (proj32_1 pa).
have od1 := OS_pow OS_omega oa.
have od2 := OS_pow OS_omega osa.
have oc1 := (proj31 le1).
by rewrite /CNFbvo (CNFq_p1 _ _ _ nB) /CNFp_value1
   (oprod2_succ pc (OS_prod2 od1 ocn)) (oprodA pc od1 ocn) /cantor_mon
   pi (opow_sum OS_omega oc1 oa) av (opow_sum OS_omega OS1 osa)
   (opowx1 OS_omega)(oprodA (OS_pow OS_omega oc1) OS_omega od2)
   (oprodA pc OS_omega od2) - (CNF_prod_pr2 pnB ax2) - pn.
Qed.

Lemma CNFp_p3 e c n: CNFb_axo e c (succ n) -> inc n Bnat ->
  exists e' c',
   [/\ CNFp_ax e' c' n, CNFbvo e c (succ n) = CNFpv e' c' n,
     (forall i, i <c n -> e' i = e (succ i) -o (e i) /\ c' i = c (succ i)),
       e' n = e \0c & c' n = c \0c].
Proof.
move => ax nB; move: n nB ax; apply: cardinal_c_induction.
  move => h0; exists e,c.
  move: (h0) (succ_positive \0c) => [[_ h1 h2 _] h4] sp.
  move: (h1 _ sp) (h2 _ sp) (h4 _ sp) => a1 a2 a4.
  have oc:= OS_prod2 (OS_pow OS_omega a1) (proj31_1 a2).
  rewrite /CNFpv CNFp_0 succ_zero /CNFbvo.
  rewrite (CNFq_p3 (proj1 h0) (card_lt_succ BS0)) (oprod1r oc).
  split => //; [split | by move => i /card_lt0].
  - by move => i /card_lt0.
  - by move => i /card_le0 ->.
  - exact.
move => n nB Hrec ax.
move: (CNFp_p2 ax (BS_succ nB) (@succ_nz n)) => [pa1 pa2 pa3 ->].
move: (Hrec (CNFb_p5 (BS_succ nB) ax)) => [e' [c' [ax3 -> h1 h2 h3]]].
set sa := _ -o _; set sb := c (succ n).
move: (proj32_1 pa1) (proj32_1 pa2) => osa osb.
have of3:=(OS_succ (OS_prod2 (OS_pow OS_omega osa) osb)).
have of1:ordinalp (omega0 ^o e' n *o c' n).
   rewrite h2 h3; exact:(CNFq_p0 (proj1 ax) (succ_positive (succ n))).
pose e1 i := Yo (i = (succ n)) (e \0c) (Yo (i = n) sa (e' i)).
pose c1 i := Yo (i = (succ n)) (c \0c) (Yo (i = n) sb (c' i)).
have eqA: e1 (succ n) = e' n by rewrite /e1; Ytac0.
have eqB: c1 (succ n) = c' n by rewrite /c1; Ytac0.
rewrite - h2 - h3.
exists e1,c1.
have nn:= (proj2 (card_lt_succ nB)).
rewrite /CNFpv (CNFp_r _ _ nB) eqA eqB {3} /e1 {3} /c1;repeat Ytac0.
have ->: CNFpv1 e1 c1 n = CNFpv1 e' c' n.
   apply:(oprod_expansion_exten nB) => i lin.
   move: (proj2(card_lt_leT lin (card_le_succ nB))) (proj2 lin) => ha hb.
   by rewrite /e1 /c1; repeat Ytac0.
rewrite (oprodA of1 (OS_CNFp1 (CNFp_ax_ax1 ax3) nB) of3).
have aux: forall i, i <c succ n ->
      (i <> succ n /\ (i = n \/ (i <> n /\ i <c n))).
  move => i h ; split; first by exact (proj2 h).
  move /(card_lt_succ_leP nB): h => lt1.
  by case: (equal_or_not i n) => hc; [left | right].
split; try reflexivity; last first.
   move => i /aux [ha hb];rewrite /e1 /c1; Ytac0; Ytac0; case: hb.
     by move => ein; Ytac0; Ytac0; rewrite ein /sa (cpred_pr2 nB).
   by move => [ein hb]; Ytac0; Ytac0; apply: (h1 _ hb).
move: ax3 => [pa pb pc]; hnf; rewrite eqA; split; last exact.
  move => i /aux [ha hb];rewrite /e1; Ytac0; case: hb.
    by move => ein; Ytac0.
  by move => [ein hb]; Ytac0; apply: pa.
move => i isn; rewrite /c1; case (equal_or_not i (succ n)) => eq1; Ytac0.
  rewrite - h3; apply:pb; fprops.
case: (equal_or_not i n) => eq2; Ytac0; first done.
by move /(card_lt_succ_leP nB): (conj isn eq1) => /pb.
Qed.

Lemma CNFp_exists x: \0o <o x ->
  exists e c n, [/\ CNFp_ax e c n, inc n Bnat & x = CNFpv e c n].
Proof.
move=> xp.
move: (the_CNF_p0 (proj32_1 xp)) => [ax0 xv].
move: (CNFB_ax_simp ax0) (the_CNF_p2 xp) => [pb ax] [nB pc].
rewrite pc in ax.
move:(CNFp_p3 ax nB) => [e [c [sa sb _ _ _]]].
by exists e,c, (cpred (P (the_CNF x))); rewrite - sb - pc.
Qed.

Lemma CNFp_p4 e c n:
   CNFp_ax e c n -> inc n Bnat ->
   exists e' c',
   [/\ CNFb_axo e' c' (succ n),
       CNFbvo e' c' (succ n) = CNFpv e c n,
       forall i, i <c n -> e i = e' (succ i) -o (e' i) /\ c i = c' (succ i),
       e n = e' \0c & c n = c' \0c].
Proof.
move=> axy.
pose e1 := induction_term (fun i z => z +o (e i)) (e n).
pose c1 i := Yo (i = \0c) (c n) (c (cpred i)).
move => nB.
have pe: c n = c1 \0c by rewrite /c1; Ytac0.
have pd: e n = e1 \0c by rewrite /e1 induction_term0.
have hc: forall i, inc i Bnat -> e1 (succ i) = e1 i +o e i.
   by move=> i iB; rewrite /e1 induction_terms.
have ose: forall i, i<c n -> ordinalp (e i).
  move => i lin; move:(BS_lt_int lin nB) => iB.
  move: axy => [a1 a2 a3]; exact(proj32_1 (a1 _ lin)).
have ose1: forall i, i <=c n -> ordinalp (e1 i).
  move => i lin; move:(BS_le_int lin nB) => iB.
  move: axy => [a1 a2 a3].
  move: i iB lin; apply: cardinal_c_induction.
    by rewrite - pd => _.
  move => i iB hr sin; rewrite (hc _ iB).
  have lin:= (card_lt_leT (card_lt_succ iB) sin).
  exact: (OS_sum2 (hr (proj1 lin)) (ose _ lin)).
have pc: forall i,i <c n -> e i = e1 (succ i) -o e1 i /\ c i = c1 (succ i).
  move => i lin; have iB:=(BS_lt_int lin nB); split; last first.
    by rewrite /c1 (cpred_pr2 iB) (Y_false (@succ_nz i)).
  by rewrite (hc _ iB) (odiff_pr1 (ose1 _ (proj1 lin)) (ose _ lin)).
have pc2: forall i, i <c succ n -> i <> \0c ->
   [/\ (cpred i <c n), e1 i = e1 (cpred i) +o e (cpred i) & c1 i = c(cpred i)].
  move => i lin inz.
  move: (cpred_pr (BS_lt_int lin (BS_succ nB)) inz) => [pa pb].
  rewrite pb in lin; move/(card_succ_succ_ltP pa nB): lin => sin.
  by rewrite {2} pb - (hc _ pa) /c1 (Y_false inz).
have axx: CNFb_axo e1 c1 (succ n).
  move: axy => [a1 a2 a3].
  have [sa sb]:= (a2 _ (card_leR (CS_Bnat nB))).
  split; first split.
  - apply: ord_le_2omega.
  - by move => i /(card_lt_succ_leP nB); apply: ose1.
  - move => i isn; case (equal_or_not i \0c) => eiz; first by rewrite eiz - pe.
    move:(pc2 _ isn eiz) => [sc _ ->]; exact: (proj2 (a2 _ (proj1 sc))).
  - move => i iB ssin.
    move: (pc2 _ ssin (@succ_nz i)); rewrite (cpred_pr2 iB); move => [sc sd _].
    have oe:=(ose1 _ (proj1 sc)).
    by move: (osum_Meqlt (a1 _ sc) oe); rewrite (osum0r oe) sd.
  - move => i isn; case (equal_or_not i \0c) => eiz; first by rewrite eiz - pe.
    move:(pc2 _ isn eiz) => [sc _ ->];exact: (proj1 (a2 _ (proj1 sc))).
move: (CNFp_p3 axx nB) => [e' [c' [ ax' sv qa qb qc]]].
have sv2: CNFbvo e1 c1 (succ n) = CNFpv e c n.
  rewrite sv /CNFpv qb qc pe pd; apply: f_equal.
  apply:(oprod_expansion_exten nB) => i lin.
  by move: (qa _ lin) (pc _ lin) => [-> ->] [-> ->].
by exists e1, c1.
Qed.

Lemma CNFp_unique e c n e' c' n' :
   CNFp_ax e c n -> CNFp_ax e' c' n' -> inc n Bnat -> inc n' Bnat ->
   CNFpv e c n = CNFpv e' c' n' ->
   [/\ n = n', e n = e' n, c n = c' n, same_below e e' n & same_below c c' n].
Proof.
move=> ax1 ax2 nB n'B eq.
move: (CNFp_p4 ax1 nB) => [e1 [c1 [pa pb pc pd pe]]].
move: (CNFp_p4 ax2 n'B) => [e1' [c1' [pa' pb' pc' pd' pe']]].
rewrite -pb -pb' in eq.
move: (CNFb_unique (BS_succ nB) (BS_succ n'B) pa pa' eq) => [sa sb].
have nn:= (succ_injective1 (CS_Bnat nB) (CS_Bnat n'B) sa).
have [a1 a2]: (e n = e' n /\ c n = c' n).
     rewrite pd pe nn pd' pe'; exact:(sb _ (succ_positive n)).
rewrite - nn in pc';split.
+ exact.
+ exact.
+ exact.
+ move => i lin;rewrite (proj1 (pc _ lin)) (proj1 (pc' _ lin)).
  rewrite (proj1 (sb _ (card_lt_leT lin (card_le_succ nB)))).
  move/(card_succ_succ_ltP (BS_lt_int lin nB) nB): lin => h.
  by rewrite (proj1 (sb _ h)).
+ move => i lin; rewrite (proj2 (pc _ lin)) (proj2 (pc' _ lin)).
  move/(card_succ_succ_ltP (BS_lt_int lin nB) nB): lin => h.
  exact (proj2 (sb _ h)).
Qed.

Lemma osum2_commutes a b: ordinalp a -> ordinalp b ->
 ((a +o b = b +o a) <-> (exists c n m,
  [/\ ordinalp c, inc n Bnat, inc m Bnat, a = c *o n & b = c *o m])).
Proof.
move=> oa ob; split; last first.
  move=> [c [n [m [oc nB mB av bv]]]].
  have on: ordinalp n by apply: OS_cardinal; fprops.
  have om: ordinalp m by apply: OS_cardinal; fprops.
  rewrite av bv - (osum_prodD on om oc) - (osum_prodD om on oc).
  by rewrite (osum2_2int nB mB)(osum2_2int mB nB) csumC.
move=> hc.
case: (ord_zero_dichot oa) => az.
  exists b; exists \0c; exists \1c;split;fprops.
    by rewrite az (oprod0r).
  by rewrite (oprod1r ob).
case: (ord_zero_dichot ob) => bz.
  exists a; exists \1c; exists \0c;split;fprops.
    by rewrite (oprod1r oa).
  by rewrite bz (oprod0r).
move: (the_CNF_p0 oa) => [ax0 xv].
move: (the_CNF_p2 az)(CNFB_ax_simp ax0) => [nB pc][].
move: nB xv; rewrite /CNFBv {2 3 4} pc.
set ea := Vg _; set ca := Vg _; set n := cpred _ => nB av snB axa.
move: (the_CNF_p0 ob) => [ax0' xv'].
move: (the_CNF_p2 bz)(CNFB_ax_simp ax0') => [nB' pc'][].
move: nB' xv'; rewrite /CNFBv {2 3 4} pc'.
set eb := Vg _; set cb := Vg _; set m := cpred _ => mB bv smB axb.
clear pc pc' ax0 ax0'.
set ean := ea n; set can := ca n; set ebn := eb m; set cbn := cb m.
move: (card_lt_succ nB) (card_lt_succ mB) => snn smm.
move: (axa) (axb) => [[_ a1 a2 a3] a4] [[_ b1 b2 b3] b4].
move: (a1 _ snn) (b1 _ smm) => oean oebn.
case: (ord_le_to_ell oean oebn); last first => cpe.
    move: (ord_negl_p8 axb axa mB nB cpe); rewrite av bv /ord_negl - hc => bad.
    by case: (proj2 bz); rewrite (osum_a_ab oa ob bad).
  move: (ord_negl_p8 axa axb nB mB cpe); rewrite av bv /ord_negl hc => bad.
  by case: (proj2 az);rewrite (osum_a_ab ob oa bad).
move: (CNF_sum_pr3 mB nB axb axa cpe).
move: (CNF_sum_pr3 nB mB axa axb (esym cpe)).
move => [ax1 v1][ax2]; rewrite av bv - hc - av -bv v1 => eq2.
move: (CNFb_unique smB snB ax1 ax2 eq2) => [r1 r2].
move: (succ_injective1 (CS_Bnat mB) (CS_Bnat nB) r1) => nm.
rewrite r1 in r2.
move: (a2 _ snn) (a4 _ snn) (b2 _ smm) (b4 _ smm) => cao cap cbo cbp.
move: (CNF_change_n_ax n ord_lt_01 ord_lt_1omega axa).
set cx:= cnf_change_n ca n \1o => ax3.
exists (CNFbvo ea cx (succ n)), can,cbn.
split.
+ exact: (OS_CNFb snB ax3).
+ by apply /olt_omegaP.
+ by apply /olt_omegaP.
+ rewrite (proj2 (CNF_prod_pr1 cap cao nB ax3)).
  apply:(osum_expansion_exten snB) => i isn.
  rewrite/cantor_mon /cx /cnf_change_n; Ytac0; Ytac h; last by Ytac0.
  by rewrite h (oprod1l (proj31_1 cao)).
have st: forall i, i <c n -> eb i = ea i /\ cb i = ca i.
  move => i lin; have sin:= (card_lt_leT lin (card_le_succ nB)).
  move: lin => [_] nin; move:(r2 _ sin).
  by rewrite /cnf_m /cnf_change_n nm; repeat Ytac0.
have <-: CNFbvo eb (cnf_change_n cb m \1o) (succ m) =
    CNFbvo ea cx (succ n).
  rewrite nm; apply:(osum_expansion_exten snB) => i isn.
  rewrite /cantor_mon/cx/cnf_change_n;Ytac h; Ytac0; first by rewrite h cpe nm.
  move /(card_lt_succ_leP nB): isn => lin.
  by move: (st _ (conj lin h)) => [-> ->].
move: (CNF_change_n_ax m ord_lt_01 ord_lt_1omega axb) => ax4.
rewrite (proj2 (CNF_prod_pr1 cbp cbo mB ax4)).
apply:(osum_expansion_exten smB) => i ism.
rewrite /cantor_mon /cnf_change_n; Ytac0; Ytac h; last by Ytac0.
by rewrite h (oprod1l (proj31_1 cbo)).
Qed.

Definition oprod_comm x y := (x *o y = y *o x).

Lemma oprod2_comm1 ex cx n ey cy m
  (x:= CNFbvo ex cx (succ n)) (y:= CNFbvo ey cy (succ m)):
  CNFb_axo ex cx (succ n) -> CNFb_axo ey cy (succ m) ->
  inc n Bnat -> inc m Bnat -> ey \0c = \0o -> \0o <o ex \0c ->
  oprod_comm x y -> y = \1o.
Proof.
move => ax ay nB mB h1 h2.
move: (CNF_prod_pr5 mB nB ay ax h2) => [ax1 e1].
move: (CNF_prod_pr6 nB mB ax ay h1) => [ax2 e2].
rewrite /oprod_comm {1} e2 e1 => e3.
have nmB:=(BS_sum nB mB).
move:(CNFb_unique (BS_succ nmB) (BS_succ nB) ax2 ax1 e3) => [e4 e5].
move: (succ_injective1 (CS_Bnat nmB) (CS_Bnat nB) e4).
rewrite - {2} (bsum0r nB) => eq3.
move: (csum_simplifiable_left nB mB BS0 eq3) => mz.
rewrite /y mz succ_zero /CNFbvo (CNFq_p3 (proj1 ay) (succ_positive m)).
rewrite /cantor_mon h1 opowx0.
have nsm:= (card_lt_succ nB).
have h:n <c succ (n +c m).
  have hk :=(card_lt_leT nsm (csum_M0le m (proj32_1 nsm))).
  by rewrite - (csum_via_succ1 _ nB).
move: (proj2 (e5 n h)); rewrite /cnf_m/cnf_change_n; Ytac0; Ytac0 => e6.
move: ((proj2 ax) _ nsm) => cnz.
have ocy:= (proj32_1 ((proj2 ay) _ (succ_positive m))).
by rewrite (oprod2_simpl1 ocy cnz e6) (oprod1r OS1).
Qed.

Lemma oprod2_comm2 e c n mu
  (x:= CNFbvo e c (succ n)) (y := (omega0 ^o mu) *o x):
  CNFb_axo e c(succ n) -> inc n Bnat -> \0o <o e \0c -> ordinalp mu ->
  (e n) +o mu = mu +o (e n) -> oprod_comm x y.
Proof.
move=> ax nB pxnz om c1.
have oe: ordinalp (e n).
   move: ax => [[_ h1 _ _] _]; apply: (h1 _ (card_lt_succ nB)).
have oomu:= OS_pow OS_omega om.
have ooe:= OS_pow OS_omega oe.
have ox:= (OS_CNFb (BS_succ nB) ax).
have oo:= OS_omega.
case: (ord_zero_dichot om) => muz; first by rewrite /y muz opowx0 (oprod1l ox).
move: (ord_rev_pred muz); set t:= _ -o _; move=> [ot tv].
have oot:= OS_pow oo ot.
have oot1:= OS_prod2 oo oot.
rewrite /oprod_comm - (oprodA oomu ox ox) (CNF_prod_pr4 nB nB ax ax pxnz).
rewrite (oprodA oomu ooe ox) - (opow_sum oo om oe) - c1 (opow_sum oo oe om).
rewrite /y tv (opow_sum oo OS1 ot) (opowx1 OS_omega).
rewrite (oprodA ox oot1 ox) (oprodA ox oo oot) (CNF_prod_pr2 nB ax).
by rewrite - (oprodA ooe oo oot).
Qed.

Definition oprod2_comm_P4 x y :=
  exists n e c gamma c1 c2,
  [/\ CNFb_axo e c (succ n), \0o <o e \0c, inc n Bnat, ordinalp gamma &
  [/\ inc c1 Bnat, inc c2 Bnat,
  x = CNFbvo e c (succ n),
  e n = gamma *o c1 &
  y = omega0 ^o (gamma *o c2) *o x]].

Lemma oprod2_comm3 x y: oprod2_comm_P4 x y -> oprod_comm x y.
Proof.
move=> [n [e [c [g [c1 [c2 [axn limX nB og [c1B c2B xv lcx yv]]]]]]]].
set mu:=(g *o c2).
have oc1:= Bnat_oset c1B.
have oc2:= Bnat_oset c2B.
have omu:=(OS_prod2 og oc2).
have coms: (e n) +o mu = mu +o (e n).
  rewrite lcx /mu - (osum_prodD oc1 oc2 og) - (osum_prodD oc2 oc1 og).
  by rewrite (osum2_2int c1B c2B) (osum2_2int c2B c1B) csumC.
by move: (oprod2_comm2 axn nB limX omu coms); rewrite /= -xv /mu -yv.
Qed.

Lemma oprod2_comm4 ex cx n ey cy m
  (x := CNFbvo ex cx (succ n)) (y := CNFbvo ey cy (succ m)):
  CNFb_axo ex cx (succ n) -> CNFb_axo ey cy (succ m) ->
  inc n Bnat -> inc m Bnat -> \0o <o (ex \0o) -> \0o <o ey \0c ->
  oprod_comm x y ->
  (oprod2_comm_P4 x y \/ oprod2_comm_P4 y x).
Proof.
move=> ax ay nB mB tcX tcY.
move: (CNF_prod_pr5 mB nB ay ax tcX) => [ax2 eq2].
move: (CNF_prod_pr5 nB mB ax ay tcY) => [ax1 eq1].
rewrite /oprod_comm {1} eq1 eq2 => eq3 {eq1 eq2}.
move: (CNFb_unique (BS_succ mB) (BS_succ nB) ax1 ax2 eq3) =>[e1 e2].
have mn:=(succ_injective1 (CS_Bnat mB) (CS_Bnat nB) e1).
move:(OS_CNFb (BS_succ nB) ax) (OS_CNFb (BS_succ mB) ay) => ox oy.
rewrite mn in e2 ay.
have nn:= card_lt_succ nB.
move: (ax)(ay) => [[_ h1 _ _]_][[_ h2 _ _] _].
move: (h1 _ nn) (h2 _ nn) => odx ody.
move: (proj1 (e2 _ nn)); rewrite (osum2_commutes odx ody).
move => [c [c1 [c2 [oc c1B c2B p1v p2v]]]].
move:(BS_diff c1 c2B) (BS_diff c2 c1B).
set c3:= c2 -c c1; set c4:= c1 -c c2 => c3B c4B.
move:(Bnat_oset c1B)(Bnat_oset c2B) => oc1 oc2.
move:(Bnat_oset c3B)(Bnat_oset c4B) => oc3 oc4.
have disj: ((c1 +c c3 = c2 /\ c4 = \0c) \/ (c2 +c c4 = c1 /\ c3 = \0c)).
  case: (Bnat_to_ee c1B c2B) => le1.
   by left; split; [ apply: (cdiff_pr le1) | apply: (cdiff_wrong le1) ].
   by right; split; [ apply: (cdiff_pr le1) | apply: (cdiff_wrong le1) ].
have [t [ot tp1 tp2]]: exists t, [/\ ordinalp t, c1 = t +o c4 & c2 = t +o c3].
   move:(esym (osum0r oc1))(esym (osum0r oc2)) => sa sb.
   move:(esym (osum2_2int c1B c3B))(esym (osum2_2int c2B c4B)) => sc sd.
   by case: disj;move => [<- ->]; [exists c1 | exists c2]; split.
move: (p1v); rewrite tp1 (osum_prodD ot oc4 oc) => p1.
move: (p2v); rewrite tp2 (osum_prodD ot oc3 oc) => p2.
have occ3: ordinalp (c *o c3) by fprops.
have occ4: ordinalp (c *o c4) by fprops.
have sYv: (omega0 ^o (c *o c3)) *o x = (omega0 ^o (c *o c4)) *o y.
  move: (proj2 (CNF_se_p (BS_succ nB) ax occ3)) => sa.
  move: (proj2 (CNF_se_p (BS_succ nB) ay occ4)) => sb.
  rewrite /y mn -{1} sa - sb; apply: (osum_expansion_exten (BS_succ nB)).
  move => i lin; move: (e2 _ lin) => [].
  move: (ax)(ay) => [ [ _ r4 _ _] _] [ [ _ s4 _ _] _].
  move: (r4 _ lin) (s4 _ lin) => oa ob.
  move:(OS_prod2 oc ot) (OS_prod2 oc oc4) (OS_prod2 oc oc3) => o1 o2 o3.
  rewrite p1 p2 - (osumA o1 o2 ob) - (osumA o1 o3 oa).
  move/(osum2_simpl (OS_sum2 o2 ob) (OS_sum2 o3 oa) o1).
  by rewrite /cantor_mon => -> ->.
case: disj; move=> [pr1 pr2].
  left; exists n, ex, cx, c,c1, c3.
   rewrite sYv pr2 (oprod0r) opowx0 (oprod1l oy) -p1v.
   by split; last split ;try reflexivity.
right; exists m, ey, cy, c, c2, c4.
rewrite - sYv pr2 (oprod0r) opowx0 (oprod1l ox) -/x -/y mn.
by split; last split; try reflexivity.
Qed.

Lemma oprod2_comm5 ex cx n ey cy m
  (x := CNFbvo ex cx (succ n)) (y := CNFbvo ey cy (succ m)):
  CNFb_axo ex cx (succ n) -> CNFb_axo ey cy (succ m) ->
  ex \0c = \0o -> ey \0c = \0o -> n = \0c -> inc m Bnat -> oprod_comm x y ->
  (x = \1o \/ (finite_o x /\ finite_o y)).
Proof.
move=> ax ay limX limY nz mB.
have nB: inc n Bnat by rewrite nz; fprops.
have [fx xv]: finite_o x /\ x = cx \0c.
  move: (ax)(succ_positive n) => [[_ _ h _] _] sn.
  move: (h _ sn) => cp.
  rewrite /x nz succ_zero /CNFbvo (CNFq_p3 (proj1 ax) sn).
  rewrite /cantor_mon limX opowx0 (oprod1l (proj31_1 cp)).
  split; [ by apply /omega_P2; apply /(ord_ltP OS_omega) | exact].
case: (p_or_not_p (m <c succ n)) => mn.
  move/(card_lt_succ_leP nB): mn; rewrite nz => /card_le0 mz.
  right; split; first by exact.
  move: (ay)(succ_positive m) => [[_ _ h _] _] sm.
  move: (h _ sm) => cp.
  rewrite /y mz succ_zero /CNFbvo (CNFq_p3 (proj1 ay) sm).
  rewrite /cantor_mon limY opowx0 (oprod1l (proj31_1 cp)).
  by apply /omega_P2; apply /(ord_ltP OS_omega).
move:(CNF_prod_pr6 nB mB ax ay limY) => [ax2 eq1].
move:(CNF_prod_pr6 mB nB ay ax limX) => [ax1 eq2].
rewrite /oprod_comm {1} eq1 eq2 => eq3 {eq1 eq2}.
have cm:=(CS_Bnat mB).
have mnn: m +c n = m by rewrite nz (csum0r cm).
have ha: m = (succ (m -c succ n)).
  rewrite nz succ_zero -(cpred_pr4 cm); apply: (proj2 (cpred_pr mB _)).
  by move => mz; case: mn; rewrite mz; apply: succ_positive.
rewrite (csumC n m) in ax1 ax2 eq3.
rewrite mnn in ax1 ax2 eq3.
move: (BS_succ mB) (card_lt_succ mB) => smB lt1.
move: (proj2 (CNFb_unique smB smB ax2 ax1 eq3)) => hsv.
move: (proj2 (hsv _ (card_lt_succ mB))); rewrite /cnf_m /cnf_change_n - ha.
repeat Ytac0; rewrite - xv;move => eq2;left.
exact: (oprod2_simpl1 (proj1 fx) (proj2 ay _ lt1) (esym eq2)).
Qed.

Definition CNF_mpe ex ey n m :=
  fun z => Yo (z = (succ n +c m)) (ex (succ n))
     (Yo (z = n) (ex n) (Yo (z <c n) (ex z) (ey (z -c (succ n))))).
Definition CNF_mpc cx cy n m :=
  fun z => Yo (z = (succ n +c m)) (cx (succ n))
     (Yo (z = n) (cx n *o cy m) (Yo (z <c n) (cx z) (cy (z -c (succ n))))).

Lemma CNFp_pg ex cx n ey cy m
  (ez := CNF_mpe ex ey n m) (cz:=CNF_mpc cx cy n m):
  CNFp_ax ex cx (succ n) -> CNFp_ax ey cy m -> inc n Bnat-> inc m Bnat ->
  (CNFp_ax ez cz (succ n +c m) /\
  (cx (succ n) *o (CNFpv1 ex cx (succ n))) *o ((cy m) *o (CNFpv1 ey cy m))
   = (cx (succ n)) *o CNFpv1 ez cz (succ n +c m)).
Proof.
move=> ax ay nB mB.
move:(ax) (ay) => [p1 p2 p3] [p4 p5 p6].
have snB:= BS_succ nB.
have ltnsn:=(card_lt_succ nB).
have lenn:= card_leR (proj32_1 ltnsn).
have cnn:=(CS_Bnat nB).
have axz: CNFp_ax ez cz (succ n +c m).
  have aux: forall i, i <c succ n +c m -> i <> n -> ~ i <c n ->
    (i -c (succ n) <c m).
   move => i pa.
   case: (card_le_to_ell cnn (proj31_1 pa)) => //.
     by move => ->.
   move => /(card_le_succ_ltP _ nB) => ha _ _.
   rewrite csumC in pa; have hh:= (BS_sum mB snB).
   exact: (cdiff_increasing3 mB (BS_lt_int pa hh) ha pa).
  split.
  + move => i isn; rewrite /ez /CNF_mpe.
     move: (proj2 isn) => ha; Ytac0; clear ha.
     Ytac h0; first by apply:(p1 _ ltnsn).
     Ytac h3; first by apply:(p1 _ (card_lt_leT h3 (proj1 ltnsn))).
     exact: (p4 _ (aux _ isn h0 h3)).
  + move => i isn; rewrite /cz /CNF_mpc.
     Ytac h1; [by apply: (p2 _ lenn) | Ytac h0 ].
     move:(p2 _ (proj1 ltnsn))(p5 _ (card_leR (CS_Bnat mB))) => [s1 s2][s3 s4].
     by split; [ apply: oprod2_pos| apply: oprod2_lt_omega].
    Ytac h3; first by apply:(p2 _ (card_leT (proj1 h3) (proj1 ltnsn))).
    exact: (p5 _ (proj1 (aux _ (conj isn h1) h0 h3))).
  + rewrite /ez /CNF_mpe; Ytac0;apply: p3.
split; first by exact.
move: (CNFp_ax_ax1 ax) => ax'.
have ox:=(OS_CNFp1 ax' snB).
have oy:=(OS_CNFp1 (CNFp_ax_ax1 ay) mB).
have ocy :=(proj32_1 (proj1 (p5 _ (card_leR (CS_Bnat mB))))).
have occ :=(proj32_1 (proj1 (p2 _ lenn))).
rewrite - (oprodA occ ox (OS_prod2 ocy oy)) (oprodA ox ocy oy); apply: f_equal.
rewrite (CNFp_A snB mB (CNFp_ax_ax1 axz)).
apply: f_equal2; last first.
  apply: (oprod_expansion_exten mB) => i im.
  rewrite /CNFp_value1/cz/ez/CNF_mpe /CNF_mpc.
  have iB:= (BS_lt_int im mB).
  move: (proj2 (csum_Mlteq snB mB im));rewrite (csumC m).
  move:(csum_Mlteq nB (BS_succ iB) (succ_positive i)).
  rewrite (csum0l cnn) (cdiff_pr1 iB snB) (csum_via_succ _ nB).
  rewrite - (csum_via_succ1 _ iB); move => [sa sb] sc.
  repeat Ytac0; Ytac h; Ytac0; [ co_tac | done ].
have nn1: n <c (succ n) +c m.
  rewrite (csum_via_succ1 _ nB); apply /(card_lt_succ_leP (BS_sum nB mB)).
  apply: (csum_M0le _ cnn).
have nn2: ~(n = succ n +c m) by move: (proj2 nn1).
move: (axz) => [p7 p8 p9].
move: (p2 _ (proj1 ltnsn)) (p1 _ ltnsn) => [s5 s6] s4.
move: (p7 _ nn1) (p8 _ (proj1 nn1)) => s1 [s2 s3].
move: (OS_CNFp0 (CNFp_ax_ax1 ax) (card_lt_succ nB)).
rewrite (CNFp_r _ _ nB)(CNFp_r _ _ nB)(CNFp_pr1 s4 s5 s6)(CNFp_pr1 s1 s2 s3).
move => o2.
rewrite -(oprodA (OS_CNFp1r ax' snB (proj1 ltnsn)) o2 ocy); apply: f_equal2.
  apply: (oprod_expansion_exten nB) => i lin.
  rewrite / CNFp_value1 /ez/cz/CNF_mpe/CNF_mpc.
  move:(proj2 (card_lt_leT lin (proj1 nn1))) (proj2 lin) => lt1 ne1.
  by repeat Ytac0.
rewrite /CNFp_value2/ez/cz/CNF_mpe/CNF_mpc;repeat Ytac0.
move:(proj32_1 s4) (proj32_1 s5) => oex ocx.
exact (esym (oprodA (OS_succ (OS_pow OS_omega oex)) ocx ocy)).
Qed.

Definition CNFp_ax4 e c n x :=
  [/\ CNFp_ax e c (succ n), inc n Bnat, e (succ n) = \0c &
    x = (c (succ n)) *o CNFpv1 e c (succ n)].

Lemma CNFp_ph ex cx n ey cy m x y
  (ez1 := CNF_mpe ex ey n (succ m)) (cz1:=CNF_mpc cx cy n (succ m))
  (ez2 := CNF_mpe ey ex m (succ n)) (cz2:=CNF_mpc cy cx m (succ n)):
  CNFp_ax4 ex cx n x -> CNFp_ax4 ey cy m y ->
  oprod_comm x y ->
   [/\ (same_below ez1 ez2 (succ n +c succ m)),
       (same_below cz1 cz2 (succ n +c succ m)),
    ( n = m -> x = y) &
    ( m <c n -> exists ez cz p z,
       [/\ CNFp_ax4 ez cz p z, x = z *o y, z *o y = y *o z & p <c n])].
Proof.
move=> [ ax nB exn xv] [ ay mB eym yv].
move:(BS_succ mB)(BS_succ nB) => smB snB.
have sc1:= (csumC (succ m)(succ n)).
have eq0:= (erefl(succ n +c succ m)).
move: (CNFp_pg ax ay nB smB) (CNFp_pg ay ax mB snB).
rewrite - {1} xv - {1} yv; move => [at1 eq1].
rewrite - {1} xv - {1} yv; move => [at2 eq2].
rewrite /oprod_comm eq1 eq2; clear eq1 eq2.
move: (CNFp_unique at1 at2 (BS_sum snB smB) (BS_sum smB snB)); rewrite /CNFpv.
set z1 := CNFpv1 _ _ _; set z2 := CNFpv1 _ _ _.
rewrite /CNF_mpe /CNF_mpc sc1 4!(Y_true eq0) exn eym opowx0.
have oq1: ordinalp (cx (succ n)).
  by move: ax => [_ a2 _]; move: (a2 _ (card_leR (CS_succ n))) => [[[]]].
have oq2: ordinalp (cy (succ m)).
  by move: ay => [_ a2 _]; move: (a2 _ (card_leR (CS_succ m))) => [[[]]].
rewrite (oprod1l oq1) (oprod1l oq2) -{4 7} sc1.
rewrite -{1} exn - eym => eq1 eq3;move: (eq1 eq3) => [_ me1 me] {eq1}.
rewrite -{1} me1 => sae sac.
split; [ exact sae | exact sac | |].
  move => enm; rewrite - enm in sac sae oq2.
  rewrite xv yv - enm.
  have saec: forall i, i<c succ n -> (cy i = cx i /\ ey i = ex i).
    move => i lt1.
    have ha:= (csum_Mlteq snB snB lt1).
    have iB:= (BS_lt_int lt1 snB).
    have eq2:= (cdiff_pr1 iB snB).
    move: (csum_M0le i (CS_Bnat nB)); rewrite csumC => l1.
    have [hb1 hb2]: n <c i +c (succ n).
      by rewrite (csum_via_succ _ nB);apply/(card_lt_succ_leP((BS_sum iB nB))).
    case: (p_or_not_p (i +c succ n <c n)) => hb3; first by co_tac.
    move: (sae _ ha)(sac _ ha); move:(proj2 ha)=> hb.
    by rewrite eq2; repeat Ytac0;split.
  have ->: cy (succ n) = cx (succ n).
    have nsn:= (card_lt_succ nB).
    move:(proj1 (saec _ nsn)) => hc.
    move: nsn =>[hb1 hb2].
    move:(ax) =>[ _ a2 _]; move: (proj1 (a2 _ hb1)) => cxnp.
    have l1:= (csum_M0le (succ n) (CS_Bnat nB)).
    move /(card_lt_succ_leP ((BS_sum nB snB))):l1.
    rewrite - (csum_via_succ1 _ nB) => ha.
    move:(proj2 ha) => hb.
    move:(sac _ ha); repeat Ytac0; rewrite hc => hd.
    exact:(oprod2_simpl oq2 oq1 cxnp hd).
  by apply: f_equal; apply: (oprod_expansion_exten snB) => i /saec [-> ->].
move=> ltnm.
have snmnz: n -c m <> \0c by apply: (cdiff_nz ltnm).
move: ltnm => [lenm nenm];move: (cdiff_pr0 nB mB lenm) => [snmB snmp].
move: (cpred_pr snmB snmnz); set p := cpred (n -c m); move=> [pB pv1].
have spB: inc (succ p) Bnat by fprops.
set nm := (succ n) +c (succ m).
have nmB:= BS_sum snB smB.
have spm:succ p +c succ m = succ n.
  by rewrite (csum_via_succ _ mB) csumC -pv1 snmp.
have spm1: p +c succ m = n.
  by rewrite (csum_via_succ _ mB) csumC - (csum_via_succ _ pB) -pv1.
have nsm: (n -c succ m) = p by apply: cdiff_pr2 => //.
have pltn: p <c n by rewrite - spm1; apply: (finite_sum4_lt pB (@succ_nz m)).
pose ez i:= Yo (i = succ p) (ex (succ n)) (ex (i +c (succ m))).
pose cz i:= Yo (i = succ p) (cx (succ n)) (cx (i +c (succ m))).
set z:= cx (succ n) *o CNFpv1 ez cz (succ p).
have axz: (CNFp_ax4 ez cz p z).
  move:(ax) =>[ a1 a2 a3].
  have aux: forall i, i<c succ p -> i +c (succ m) <c (succ n).
    move => i isp.
     by move: (csum_Mlteq smB (BS_succ pB) isp); rewrite spm.
  hnf; rewrite /z {1 2} /ez {1 3} /cz exn;Ytac0; Ytac0.
  split; [ split | exact | exact | reflexivity ].
  + move => i lip;move:(lip) => [_ h]; Ytac0; exact:(a1 _ (aux _ lip)).
  + move => i lip; Ytac h; first exact: (a2 _ (card_leR (CS_succ n))).
    exact:(a2 _ (proj1 (aux _ (conj lip h)))).
  + Ytac0; apply: OS0.
set ea:= CNF_mpe ez ey p (succ m) ; set ca := CNF_mpc cz cy p (succ m).
have eqa: forall i, i <c (succ n) -> (ea i = ex i /\ ca i= cx i).
  move => i ism; rewrite /ea /ca.
  have ha:=(csum_Mlteq smB snB ism).
  move: (sae _ ha) (sac _ ha); rewrite sc1.
  rewrite /CNF_mpe/CNF_mpc /cz/ez spm.
  rewrite 4!(Y_false (proj2 ha)) 2!(Y_false (proj2 ism)).
  have iB:= (BS_lt_int ism snB).
  move: (csum_M0le i (CS_Bnat mB)); rewrite csumC => li1.
  move/(card_lt_succ_leP (BS_sum iB mB)):li1.
  rewrite - (csum_via_succ _ mB); move => [ha4 ha5].
  case: (p_or_not_p (i +c succ m <c m)) => ha7; first by co_tac.
  have h3:= (proj2 (card_lt_succ pB)).
  repeat Ytac0.
  rewrite (cdiff_pr1 iB smB).
  case: (card_le_to_ell (proj31_1 ism) (CS_Bnat pB)) => cip.
  + case (p_or_not_p (n <c m) ) => ht; first by co_tac.
    by rewrite cip spm1; repeat Ytac0; split.
  + move:(csum_Mlteq smB pB cip); rewrite spm1 => ha1.
    have ha6:= (proj2 (card_lt_leT cip (card_le_succ pB))).
    by move:(proj2 cip)(proj2 ha1) => ha2 ha3; repeat Ytac0; split.
  + move:(csum_Mlteq smB iB cip); rewrite spm1; move => [hb1 hb2].
    case (p_or_not_p (i +c succ m <c n)) => ht1; first by co_tac.
    have ->: (i +c succ m) -c succ n = i -c succ p.
      move/(card_le_succ_ltP i pB): cip => lt2.
      rewrite - {1} (cdiff_pr lt2) (csumC (succ p)) - csumA spm.
      apply: (cdiff_pr1 (BS_diff (succ p) iB) snB).
    move: cip => [ha8 ha9].
    case (p_or_not_p (i <c p) ) => ht; first by co_tac.
    by repeat Ytac0; split.
set eb:=(CNF_mpe ey ez m (succ p)); set cb:= (CNF_mpc cy cz m (succ p)).
have eqb: forall i, i <c (succ n) -> (ex i = eb i /\ cx i= cb i).
  move => i iln; rewrite /eb /cb.
  have ha:=(card_lt_leT iln (csum_M0le (succ m) (CS_succ n))).
  move: (sae _ ha) (sac _ ha).
  rewrite/CNF_mpe/CNF_mpc /cz/ez sc1.
  move: (proj2 iln) (proj2 ha) => nin nin2.
  rewrite (csumC (succ m)(succ p)) spm; repeat Ytac0.
  case (equal_or_not i n) => ein.
     move => _ _.
     case: (p_or_not_p (n <c m)) => h; first by co_tac.
     have h3:= (proj2 (card_lt_succ pB)).
     by rewrite ein nsm spm1; repeat Ytac0; split.
  have lin1: i <c n by split; [ apply/(card_lt_succ_leP nB) | exact ].
  repeat Ytac0.
  case : (card_le_to_ell (proj31_1 lin1) (CS_Bnat mB)) => cim.
  + by rewrite cim; repeat Ytac0; split.
  + by move: (proj2 cim) => hb; repeat Ytac0; split.
  + have smi: succ m <=c i by move/(card_le_succ_ltP i mB): cim.
     rewrite (csumC (i -c succ m)) (cdiff_pr smi).
     case: (equal_or_not (i -c succ m) (succ p)) => hu.
       by move: (cdiff_pr smi) nin; rewrite hu csumC spm => ->; case.
     move: cim => [hb hc].
     case: (p_or_not_p (i <c m)) => he; first by co_tac.
     by move => _ _; repeat Ytac0; split.
have czp: cz (succ p) = cx (succ n) by rewrite /cz; Ytac0.
move: (axz)=> [az1 az2 az3].
move: (CNFp_pg az1 ay pB smB) => [_].
have ->: CNFpv1 ea ca (succ p +c succ m) = CNFpv1 ex cx (succ n).
  by rewrite spm; apply:(oprod_expansion_exten snB) => i /eqa [-> ->].
move => sa sb.
move: sa; rewrite - {1} sb czp - xv - yv => eqc.
move: (CNFp_pg ay az1 mB spB) => [_].
rewrite - {1} yv.
have ->: CNFpv1 eb cb (succ m +c succ p) = CNFpv1 ex cx (succ n).
  by rewrite csumC spm; apply:(oprod_expansion_exten snB) => i /eqb [-> ->].
rewrite - sb - me - xv => sc.
by exists ez, cz, p, z; rewrite eqc sc; split.
Qed.

Definition oprod2_comm_P1 x y :=
  exists c n m,
    [/\ ordinalp c, inc n Bnat, inc m Bnat, x = c ^o n & y = c ^o m].

Lemma oprod2_comm6 ex cx n x ey cy m y:
  CNFp_ax4 ex cx n x -> CNFp_ax4 ey cy m y ->
  oprod_comm x y -> oprod2_comm_P1 x y.
Proof.
move => h1 h2 h3.
move:(h1)(h2) => [_ nB _ _] [_ mB _ _].
move:(BS_sum nB mB);set p := n +c m => pB.
have aux: n +c m <=c p by rewrite /p; fprops.
move: p pB aux => p pB aux.
move: p pB ex cx n x ey cy m y h1 h2 h3 aux nB mB.
have ores: forall ex cx n x, CNFp_ax4 ex cx n x -> ordinalp x.
  move=> ex cx n x [ax1 nB _ ->]; move: (ax1) => [_ ax2 _].
  move: (proj32_1 (proj1 (ax2 _ (card_leR (CS_succ n))))) => o1.
  exact: (OS_prod2 o1 (OS_CNFp1 (CNFp_ax_ax1 ax1) (BS_succ nB))).
have special: forall ex cx n x, CNFp_ax4 ex cx n x -> oprod2_comm_P1 x x.
  move=> ex cx n x /ores h; move: (opowx1 h) => p1.
  exists x; exists \1c; exists \1c; split;fprops.
apply: cardinal_c_induction.
  move=> ex cx n x ey cy m y pa pb h1 h2 nB mB.
  move: (card_le0 h2). rewrite -(osum2_2int nB mB) => nmz.
  move: (osum2_zero (Bnat_oset nB) (Bnat_oset mB) nmz) => [nz mz].
  rewrite - mz in nz;move: (CNFp_ph pa pb h1) => [_ _ ok _].
  move: (ok nz) => ok0; rewrite - ok0; apply: special pa.
move=> sm smB hrec ex cx n x ey cy m y pa pb h1 h2 nB mB.
wlog:ex cx ey cy n m x y pa pb h1 h2 nB mB / (m <=c n).
  move=> aux.
  case: (card_le_to_el (CS_Bnat mB) (CS_Bnat nB)).
    apply: aux pa pb h1 h2 nB mB.
  symmetry in h1; rewrite csumC in h2.
  move=> [lemn _]; move: (aux _ _ _ _ _ _ _ _ pb pa h1 h2 mB nB lemn).
  move => [z [xe [ye [oz xeB yeB eq1 e2]]]].
  exists z; exists ye; exists xe; split => //.
move: (CNFp_ph pa pb h1) => [_ _ pc pd].
move=> lenm; case: (equal_or_not m n) => dnm.
 symmetry in dnm;rewrite -(pc dnm); apply: (special _ _ _ _ pa).
have ltnm: m <c n by split.
move: (pd ltnm) => [Z [pZ [p [z [pe xv h3 ltpn]]]]].
have pB: inc p Bnat by move: ltpn => [lepn _];apply: (BS_le_int lepn nB).
move: (csum_Mlteq mB nB ltpn) => r1.
have: (p +c m <c (succ sm)) by co_tac.
move /(card_lt_succ_leP smB) => le1.
move: (hrec _ _ _ _ _ _ _ _ pe pb h3 le1 pB mB)=> xx.
move: xx =>[t [xe [ye [oz xeB yeB eq1 e2]]]].
exists t; exists (xe +c ye); exists ye; split;fprops.
have oxe: ordinalp xe by apply: OS_cardinal; fprops.
have oye: ordinalp ye by apply: OS_cardinal; fprops.
by rewrite xv eq1 e2 - (opow_sum oz oxe oye) (osum2_2int xeB yeB).
Qed.

Theorem oprod2_comm x y: ordinalp x -> ordinalp y ->
   ((oprod_comm x y) <->
   (x = \0o \/ y = \0o \/ (oprod2_comm_P4 x y \/ oprod2_comm_P4 y x) \/
   (finite_o x /\ finite_o y) \/ oprod2_comm_P1 x y)).
Proof.
move=> ox oy.
split; last first.
  rewrite /oprod_comm.
  move => h; case: h; first by move=> ->; rewrite (oprod0r) (oprod0l).
  case; first by move=> ->; rewrite (oprod0r) (oprod0l).
  case; first by case ;[ apply: oprod2_comm3 | symmetry; apply: oprod2_comm3 ].
  case.
    move => [/BnatP xB /BnatP yB].
    by rewrite (oprod2_2int xB yB)(oprod2_2int yB xB) cprodC.
  move=> [c [n [m [oc nB mB -> ->]]]].
  move:(Bnat_oset nB) (Bnat_oset mB) => on om.
  rewrite - (opow_sum oc on om) - (opow_sum oc om on).
  by rewrite (osum2_2int nB mB)(osum2_2int mB nB) csumC.
move=> h.
case: (ord_zero_dichot ox) => xnz; first by left.
case: (ord_zero_dichot oy) => ynz; [by right;left | right; right].
case: (equal_or_not y \1c) => y1.
  by right;right;exists x, \1o, \0c;split;fprops; rewrite ?opowx1 // y1 opowx0.
case: (equal_or_not x \1c) => x1.
 by right;right;exists y,\0c,\1c;split;fprops; rewrite ? opowx1 // x1 opowx0.
move: (the_CNF_p0 ox) => [ax0].
move: (the_CNF_p2 xnz)(CNFB_ax_simp ax0).
rewrite /CNFBv;set ex := Vg _ ; set cx := Vg _; set n' := P _; set n:= cpred _.
move => [nB nn] [n'B ax] xv.
move: (the_CNF_p0 oy)=> [ay0].
move: (the_CNF_p2 ynz)(CNFB_ax_simp ay0).
rewrite /CNFBv;set ey := Vg _ ; set cy := Vg _; set m' := P _; set m:= cpred _.
move => [mB mm] [m'B ay] yv.
clear ax0 ay0.
rewrite nn in ax xv; rewrite mm in ay yv.
have h1:oprod_comm (CNFbvo ex cx (succ n)) (CNFbvo ey cy (succ m)).
   by rewrite xv yv.
move: (ax) (ay) => [[_ ax0 _ _] _][[_ ay0 _ _] _].
move: (ax0 _ (succ_positive n)) (ay0 _ (succ_positive m)) => oex0 oey0.
case: (ord_zero_dichot oex0) => limX; last first.
  case: (ord_zero_dichot oey0) => limY; last first.
    by move: (oprod2_comm4 ax ay nB mB limX limY h1); rewrite xv yv; left.
  move:(oprod2_comm1 ax ay nB mB limY limX h1);rewrite yv => h2; contradiction.
case: (ord_zero_dichot oey0) => limY; last first.
  symmetry in h1.
  move: (oprod2_comm1 ay ax mB nB limX limY h1); rewrite xv => h2;contradiction.
case: (equal_or_not n \0c) => n1.
  move: (oprod2_comm5 ax ay limX limY n1 mB h1); rewrite xv yv.
  case => h2; [ by case: x1 | by right;left].
case: (equal_or_not m \0c) => m1.
  symmetry in h1.
  move: (oprod2_comm5 ay ax limY limX m1 nB h1); rewrite xv yv.
  case => h2; [ by case: y1 |move: h2 => [q1 q2];by right;left; split].
right; right.
move: (CNFp_exists xnz) => [ex' [cx' [n'' [ax2 n''B xv2]]]].
move: (CNFp_p4 ax2 n''B) => [e''x [c''x [ax3 xv3 ex1 eqx2 eqx3]]].
rewrite - xv2 - xv in xv3.
move: (CNFb_unique (BS_succ n''B) (BS_succ nB) ax3 ax xv3) => [sa scx].
have mn:=(succ_injective1 (CS_Bnat n''B) (CS_Bnat nB) sa).
rewrite mn in eqx2 eqx3 scx xv2 ax2; clear sa mn xv3.
move: (CNFp_exists ynz) => [ey' [cy' [m'' [ay2 m''B yv2]]]].
move: (CNFp_p4 ay2 m''B) => [e''y [c''y [ay3 yv3 ey1 eqy2 eqy3]]].
rewrite - yv2 - yv in yv3.
move: (CNFb_unique (BS_succ m''B) (BS_succ mB) ay3 ay yv3) => [sa scy].
have mn:=(succ_injective1 (CS_Bnat m''B) (CS_Bnat mB) sa).
rewrite mn in eqy2 eqy3 scy yv2 ay2; clear sa mn yv3.
move:(proj2 ax _ (succ_positive n)) (proj2 ay _ (succ_positive m)) => c1a c2a.
move:(proj32_1 c1a) (proj32_1 c2a) => oc1 oc2.
move: xv2 yv2; rewrite /CNFpv eqy2 eqx2 eqx3 eqy3.
move: (scy _ (succ_positive m))(scx _ (succ_positive n)) => [e1 e2][e3 e4].
rewrite e1 e2 e3 e4 limX limY opowx0 (oprod1l oc1) (oprod1l oc2) => xv4 yv4.
have ax5: (CNFp_ax4 ex' cx' (cpred n) x).
  move: (cpred_pr nB n1) => [qa qb].
  by hnf; rewrite - qb eqx2 eqx3 e4 e3 limX - xv4; split.
have ay5: (CNFp_ax4 ey' cy' (cpred m) y).
  move: (cpred_pr mB m1) => [qa qb].
  by hnf; rewrite - qb eqy2 eqy3 e2 e1 limY - yv4; split.
exact: (oprod2_comm6 ax5 ay5 h).
Qed.

Natural sum of ordinals

Definition CNF_exponents e n:= fun_image (Bint n) e.
Notation CNF_coefficients := CNF_exponents (only parsing).

Lemma CNF_exponentsP e n: inc n Bnat ->
   forall z, (inc z (CNF_exponents e n) <->
     exists2 i, i <c n & z = e i).
Proof.
move => nB z;split.
  by move/funI_P => [i /(BintP nB) idf ->]; exists i.
move => [i /(BintP nB) idf ->]; apply/funI_P; ex_tac.
Qed.

Lemma CNF_exponents_exten e e' n:
   inc n Bnat -> same_below e e' n ->
   CNF_exponents e n = CNF_exponents e' n.
Proof.
move => nB h.
by set_extens t; move /(CNF_exponentsP _ nB) => [i lin ->];
  apply /(CNF_exponentsP _ nB); exists i; rewrite ? (h _ lin).
Qed.

Lemma CNFq_exists3 a y (X := the_CNF a) :
  ordinalp y -> a <o (omega0 ^o y) ->
  (forall z, inc z (CNF_exponents (Vg (P (Q X))) (P X)) -> z <o y).
Proof.
move => oe ale.
move: (proj31_1 ale) => oa.
move:(the_CNF_p0 oa) => [/CNFB_ax_simp [nB ax] sb].
move => z /(CNF_exponentsP _ nB) [i lin ->].
case: (ord_zero_dichot oa) => az.
  by move:the_CNF_p1 lin; rewrite - az => -> /card_lt0.
case: (ord_le_to_el oe (OS_the_CNF_degree oa)).
  move/opow_Momega_le.
  move: (ord_le_ltT (proj1 (the_CNF_p4 az)) ale) => la lb; ord_tac.
move: (the_CNF_p2 az) => []; set m := (cpred (P (the_CNF a))) => mB mv la.
have ha: i <=c m by apply /(card_lt_succ_leP mB); rewrite - mv.
have hb: m <c P (the_CNF a) by rewrite mv; apply/card_lt_succ.
exact:(ord_le_ltT (CNF_exponents_M nB (proj1 ax) ha hb) la).
Qed.

Lemma CNF_exponents_card b e c n:
  CNFq_ax b e c n -> inc n Bnat -> n = cardinal (CNF_exponents e n).
Proof.
move => h nB.
rewrite - {1} (card_Bint nB).
set f := Lf e (Bint n) (CNF_exponents e n).
suff bf: bijection f by apply /card_eqP;exists f; split => //; rewrite /f; aw.
apply: lf_bijective.
- move => t /= td; apply /funI_P; ex_tac.
- move => u v /(BintP nB) ui /(BintP nB) vi sv.
  exact: (CNF_exponents_I nB h ui vi sv).
- by move=> y /funI_P.
Qed.

Lemma CNF_exponents_of b e c n (E := CNF_exponents e n):
  CNFq_ax b e c n -> inc n Bnat -> (finite_set E /\ ordinal_set E).
Proof.
move => h nB; split.
   by hnf; rewrite - (CNF_exponents_card h nB); apply /BnatP.
move => t /(CNF_exponentsP _ nB) [i lin ->].
move: h => [_ h1 _ _]; apply: (h1 _ lin).
Qed.

Lemma CNF_exponents_compare_expo b e c n a (E:= CNF_exponents e n) :
  CNFq_ax b e c n -> ordinalp a -> inc n Bnat ->
  [\/ inc a E, (forall t, inc t E -> t <o a), (forall t, inc t E -> a <o t)
   | (exists i, [/\ i <c n, (succ i) <c n, e i <o a & a <o e (succ i)])].
Proof.
move => H oa nB.
have K:= (CNF_exponentsP e nB).
move: (BintP nB) => K1.
move: (H) => [pa pb pc pd].
case: (equal_or_not n \0c) => nz.
  constructor 3 => t /K [i lin ->]; move: lin.
  rewrite nz => h;case: (card_lt0 h).
move: (cpred_pr nB nz); set n' := cpred n; move=> [cn' cnv].
have n'd: n' <c n by move: (card_lt_succ cn'); rewrite - cnv.
have opn:= (pb _ n'd).
case: (ord_le_to_el oa opn) => cl; last first.
  constructor 2 => t /K [j jdf ->]; move: (pb _ jdf) => ejb.
  have lt1: (j <=c n') by apply/(card_lt_succ_leP cn'); rewrite - cnv.
  exact: (ord_le_ltT (CNF_exponents_M nB H lt1 n'd) cl).
have on': ordinalp n' by apply: OS_cardinal; apply: CS_Bnat.
pose p j := j <c n /\ a <=o e j.
have pn': p n' by split.
move: (least_ordinal4 on' pn'); set j:= least_ordinal _ _.
move=> [_ [jd jl] jp].
case: (equal_or_not a (e j)) => eq1.
  by constructor 1; rewrite eq1;apply/K; exists j.
case: (equal_or_not j \0c) => jz.
  constructor 3 => t /K [k kd ->].
  have jk : j <=c k.
    rewrite jz; apply: (czero_least (proj31_1 kd)).
  exact: (ord_lt_leT (conj jl eq1) (CNF_exponents_M nB H jk kd)).
have jB:=(BS_lt_int jd nB).
move: (cpred_pr jB jz); set k := cpred j ; move=> [kB jv].
have ok:=(Bnat_oset kB).
move: (card_lt_succ kB); rewrite -jv => lkj.
have lkn:=(card_lt_leT lkj (proj1 jd)).
constructor 4; exists k; rewrite -jv;split => //.
move: (pb _ lkn) => vo; case: (ord_le_to_el oa vo) => // aux.
move: (jp _ ok (conj lkn aux)) (ordinal_cardinal_lt lkj) => sa sb; ord_tac.
Qed.

Lemma CNFq_axm_exp ex n ey m:
  inc n Bnat -> inc m Bnat ->
  CNF_exponents (cnf_m ex ey n) (n +c m)
      = (CNF_exponents ex n) \cup (CNF_exponents ey m).
Proof.
move => nB mB.
move: (BS_sum nB mB) => mkB.
rewrite /cnf_m;set_extens t.
  move /(CNF_exponentsP _ mkB) => [i limn ->]; apply /setU2_P.
  Ytac jk; first by left; apply /(CNF_exponentsP _ nB); exists i.
  right; apply /(CNF_exponentsP _ mB); exists (i -c n) => //.
  have iB:= (BS_lt_int limn mkB).
  case: (Bnat_to_el nB iB) => // lmi.
  apply: (csum_lt_simplifiable nB (BS_diff n iB) mB).
  by rewrite (cdiff_pr lmi).
move => tx; apply/(CNF_exponentsP _ mkB); case /setU2_P: tx.
  move /(CNF_exponentsP _ nB) => [i lin ->]; exists i.
    by apply: card_lt_leT (csum_M0le m (CS_Bnat nB)).
  by Ytac0.
move /(CNF_exponentsP _ mB) => [i lin ->]; exists (i +c n).
  by rewrite (csumC n m); apply: (csum_Mlteq nB mB).
Ytac h; first by move:(csum_M0le i (CS_Bnat nB)); rewrite csumC => ha; co_tac.
by rewrite (cdiff_pr1 (BS_lt_int lin mB)).
Qed.

Definition CNF_ext_coeffs (ex cx:fterm) nx (ey cy:fterm) ny :=
  (forall i, i <c nx -> exists j, [/\ j <c ny, ex i = ey j & cx i = cy j]).

Lemma CNF_exp_add_expo1 b e c n a (E:= CNF_exponents e n) :
  CNFq_ax b e c n -> ordinalp a -> inc n Bnat ->
  (forall t : Set, inc t E -> a <o t) ->
  exists e' c' n',
    [/\ CNFq_ax b e' c' n', CNF_exponents e' n'= E +s1 a, inc n' Bnat,
    CNFbv b e c n = CNFbv b e' c' n' & CNF_ext_coeffs e c n e' c' n'].
Proof.
move => ax oa nB H.
move: (CNFq_pg0 ax) => omp.
move: (ax) =>[ax1 ax2 ax3 ax4].
set ey:= fun i => (Yo (i = \0c) a (e (cpred i))).
set cy:= fun i => (Yo (i = \0c) \0o (c (cpred i))).
have aux: forall i, i <> \0c -> i <c succ n -> (cpred i) <c n.
  move => i ine lin; move: (BS_lt_int lin (BS_succ nB)) => iB.
  move: (cpred_pr iB ine) => [sa sb].
  by apply/(card_succ_succ_ltP sa nB); rewrite - sb.
have aux2: forall i, i <c n -> ( (succ i <c succ n) /\ cpred (succ i) = i).
  move => i lin; move:(BS_lt_int lin nB) => iB.
  move /(card_succ_succ_ltP iB nB): (lin) => inz.
  by rewrite (cpred_pr1 (CS_Bnat iB)).
have snB:= BS_succ nB.
have ay:CNFq_ax b ey cy (succ n).
   split.
    + exact.
    + move => i iz; rewrite /ey; Ytac h;[ exact | apply: (ax2 _ (aux _ h iz))].
    + move => i iz; rewrite /cy; Ytac h;[ exact | apply: (ax3 _ (aux _ h iz))].
    + move => i iB siz; rewrite /ey.
      rewrite (Y_false (@succ_nz i)) (cpred_pr1 (CS_Bnat iB)).
      move/(card_succ_succ_ltP iB nB): siz => inz.
      Ytac h; first by apply: H; apply /(CNF_exponentsP _ nB); exists i.
      move: (cpred_pr iB h) => [sa sb]; rewrite {2} sb;apply: ax4 => //; ue.
have h1: CNFbv b e c n =CNFbv b (fun z=> ey (succ z)) (fun z => cy (succ z)) n.
   apply: (osum_expansion_exten nB) => i lin.
   rewrite /cantor_mon /cy/ey (cpred_pr1 (proj31_1 lin)).
   by rewrite !(Y_false (@succ_nz i)).
have yv: CNFbv b e c n = CNFbv b ey cy (succ n).
  rewrite (proj33 (CNFq_Al nB ay)) - h1 /cantor_mon/ey/cy; Ytac0; Ytac0.
  rewrite oprod0r (osum0r (OS_CNFq nB ax)); reflexivity.
have ee: CNF_exponents ey (succ n) = E +s1 a.
  set_extens t.
    move/(CNF_exponentsP _ snB) => [i lin ->]; rewrite /ey; Ytac h; fprops.
    apply: setU1_r; apply/(CNF_exponentsP _ nB); move: (aux _ h lin) => h2.
    by exists (cpred i).
  case/setU1_P.
    move /(CNF_exponentsP _ nB) => [i lin ->]; apply /(CNF_exponentsP _ snB).
    move: (aux2 _ lin) => [sa sb]; exists (succ i); first by exact.
    by rewrite /ey (Y_false (@succ_nz i)) sb.
  move => ->; apply /(CNF_exponentsP _ snB).
   exists \0c; [apply succ_positive | by rewrite /ey; Ytac0].
have h2:CNF_ext_coeffs e c n ey cy (succ n).
  move => i /aux2 [sa sb]; exists (succ i).
  by rewrite /ey /cy !(Y_false (@succ_nz i)) sb; split.
by exists ey, cy, (succ n); split.
Qed.

Lemma CNF_exp_add_expo b e c n a (E:= CNF_exponents e n) :
  CNFq_ax b e c n -> ordinalp a -> inc n Bnat ->
  exists e' c' n',
    [/\ CNFq_ax b e' c' n', CNF_exponents e' n' = E +s1 a, inc n' Bnat,
    CNFbv b e c n = CNFbv b e' c' n' & CNF_ext_coeffs e c n e' c' n'].
Proof.
move=> ax oa nB.
move: (CNFq_pg0 ax) => omp.
case: (CNF_exponents_compare_expo ax oa nB) => cp.
+ exists e, c, n; split => //; first by rewrite (setU1_eq cp).
  by move => i lin; exists i.
+ pose e1 i := Yo (i = n) a (e i); pose c1 i := Yo (i = n) \0o (c i).
  have snB:= BS_succ nB.
  have nsn:= (card_lt_succ nB).
  have aux: forall i, i <c succ n -> i <> n -> i <c n.
     by move => i /(card_lt_succ_leP nB) h1 h2; split.
  have ay: CNFq_ax b e1 c1 (succ n).
    move: (ax) =>[ax1 ax2 ax3 ax4].
    split.
   + exact.
   + move => i isn; rewrite /e1; Ytac h; [ exact |apply: (ax2 _ (aux _ isn h))].
   + move => i isn; rewrite /c1; Ytac h; [ exact |apply: (ax3 _ (aux _ isn h))].
   + move => i iB siz; rewrite /e1.
     move/(card_succ_succ_ltP iB nB): siz => lin.
     rewrite (Y_false (proj2 lin)); Ytac h.
        by apply: cp;apply/(CNF_exponentsP _ nB); exists i; rewrite /e1.
     exact: (ax4 _ iB (conj (proj2 (card_le_succ_lt0P (CS_Bnat iB) nB) lin) h)).
  have ha :CNF_exponents e1 (succ n) = E +s1 a.
    rewrite /e1;set_extens t.
      move/(CNF_exponentsP _ snB) => [i lin ->]; Ytac h; fprops.
      apply: setU1_r; apply/(CNF_exponentsP _ nB).
      by move: (aux _ lin h) => lin1;exists i.
    case/(setU1_P).
      move/(CNF_exponentsP _ nB) => [i [u v] ->]; apply/(CNF_exponentsP _ snB).
      exists i; [co_tac | by Ytac0].
    move => ->; apply/(CNF_exponentsP _ snB); exists n; [exact | by Ytac0].
  have hc:CNFbv b e c n = CNFbv b e1 c1 (succ n).
     have ss:CNFbv b e c n = CNFbv b e1 c1 n.
        apply: (osum_expansion_exten nB) => i [ _ lin].
        by rewrite /cantor_mon /e1 /c1; Ytac0; Ytac0.
      rewrite (CNFq_p1 _ _ _ nB) - ss /cantor_mon /e1 /c1;Ytac0; Ytac0.
      rewrite oprod0r (osum0l (OS_CNFq nB ax)); reflexivity.
  have hd: CNF_ext_coeffs e c n e1 c1 (succ n).
    move => i [lin nin]; move: (card_le_ltT lin nsn)=> l2.
    by exists i; rewrite /e1 /c1; Ytac0; Ytac0; split.
  by exists e1,c1, (succ n); split.
+ by apply:CNF_exp_add_expo1.
+ move:cp=> [k [lkn lskn cla cga]].
  move: (cdiff_pr (proj1 lskn)) (BS_diff (succ k) nB).
  set m := _ -c _ => mn' mB; symmetry in mn'.
  move: (CNF_exponents_sM nB ax) => mono.
  rewrite mn' in ax.
  have kB:= (BS_lt_int lkn nB).
  have skB := BS_succ kB.
  move:(CS_Bnat kB) (CS_Bnat skB)(CNFq_A skB mB ax) => ck csk [].
  set e1 := (cnf_s e (succ k)); set c1:= (cnf_s c (succ k)) => ax1 ax2 xv.
  have ha: forall t, inc t (CNF_exponents e1 m) -> a <o t.
    move => t /(CNF_exponentsP _ mB) [ i ik ->].
    have eq1: e (succ k) = e1 \0c by rewrite /e1 /cnf_s (csum0l csk).
    move: (CNF_exponents_M mB ax2 (czero_least (proj31_1 ik)) ik).
    move: cga; rewrite eq1 => sa sb; ord_tac.
  have [e3 [c3 [n3 [ax3 ex3 n3B x3v cx3]]]]:=(CNF_exp_add_expo1 ax2 oa mB ha).
  have hb:(n3 = \0c \/ e (cpred (succ k)) <o e3 \0o).
    rewrite (cpred_pr1 ck).
    case: (equal_or_not \0c n3) => n3p; [by left | right].
    have: inc (e3 \0c) (CNF_exponents e3 n3).
      apply /(CNF_exponentsP _ n3B); exists \0c; [split; fprops | exact].
    rewrite ex3; case /setU1_P; last by move => ->.
    move /(CNF_exponentsP _ mB) => [i lim ->]; rewrite /e1 /cnf_s.
    have hc: k <c i +c (succ k).
      rewrite (csum_via_succ _ kB).
      apply/(card_lt_succ_leP (BS_sum (BS_lt_int lim mB) kB)).
      rewrite csumC; exact:(csum_M0le i ck).
    move: (csum_Mlteq skB mB lim);rewrite (csumC m) - mn' => hd.
    exact:(mono _ _ hc hd).
  move: (CNFq_m_ax skB n3B ax1 ax3 hb).
  set e4:= (cnf_m e e3 (succ k)); set c4:= (cnf_m c c3 (succ k)) => ax4.
  set e5 := (cnf_s e4 (succ k)); set c5:= (cnf_s c4 (succ k)).
  have sa: CNFbv b e3 c3 n3 = CNFbv b e5 c5 n3.
    apply:(osum_expansion_exten n3B) => i lin.
    have iB:= (BS_lt_int lin n3B).
    move: (csum_M0le i csk); rewrite csumC => hc.
    rewrite /cantor_mon /e5/e4 /c5/c4 /cnf_s /cnf_m (cdiff_pr1 iB skB).
    Ytac ba; Ytac0; [co_tac | exact].
  have sb: CNFbv b e c (succ k) = CNFbv b e4 c4 (succ k).
    apply:(osum_expansion_exten skB) => i lin.
    by rewrite /e4 /c4 /cnf_m /cantor_mon; Ytac0; Ytac0.
  have kn3B:= (BS_sum skB n3B).
  have hc:CNF_exponents e4 (succ k +c n3) = E +s1 a.
    rewrite (CNFq_axm_exp e e3 skB n3B) ex3 setU2_A; congr union2.
    rewrite -(CNFq_axm_exp e e1 skB mB) - mn'.
    have H: forall i, i <c n -> cnf_m e e1 (succ k) i = e i.
      move => i lin; rewrite /e1 /cnf_m /cnf_s.
      case: (card_le_to_el csk(proj31_1 lin))=> h; last by Ytac0.
      by rewrite (Y_false (card_leltA h)) csumC (cdiff_pr h).
    apply:(CNF_exponents_exten nB H).
  have hd:CNF_ext_coeffs e c n e4 c4 (succ k +c n3).
    move=> i lin; case: (card_le_to_el csk (proj31_1 lin)) => cik.
      move: (cdiff_pr cik) (BS_lt_int lin nB) => ea iB.
      move: (csum_lt_simplifiable skB (BS_diff (succ k) iB) mB).
      rewrite ea - mn' => hu; move: (hu lin) => /cx3 [j [ja]].
      rewrite /e1/c1 /cnf_s csumC ea => -> ->.
      move:(BS_lt_int ja n3B) => jB.
      move: (csum_M0le j csk) (csum_Mlteq skB n3B ja); rewrite csumC => lt1 lt2.
      case: (p_or_not_p (j +c succ k <c succ k)) => h; first by co_tac.
      rewrite csumC; exists (j +c succ k); rewrite /e4 /c4 /cnf_m; Ytac0; Ytac0.
      by rewrite (cdiff_pr1 jB skB); split.
   have ik1:= (card_lt_leT cik (csum_M0le n3 csk)).
   by exists i; rewrite /e4/c4 /cnf_m; Ytac0; Ytac0; split.
  move: (CNFq_A skB n3B ax4) => [ax5 ax6].
  move:xv; rewrite -mn' x3v sa sb => <- <-.
  exists e4, c4, (succ k +c n3); split; try reflexivity;exact.
Qed.

Lemma CNF_exp_add_expos b e c n A (E:= CNF_exponents e n) :
  CNFq_ax b e c n -> inc n Bnat -> finite_set A -> ordinal_set A ->
  exists e' c' n',
    [/\ CNFq_ax b e' c' n', CNF_exponents e' n' = E \cup A, inc n' Bnat,
    CNFbv b e c n = CNFbv b e' c' n' & CNF_ext_coeffs e c n e' c' n'].
Proof.
move => ax nB fs; move: A fs.
apply: finite_set_induction.
  move => _; exists e,c,n;split => //; first by rewrite setU2_0.
  by move => i id; exists i.
move => A a Hrec os1.
have osa: ordinal_set A by move=> t ta; apply: os1; fprops.
move: (Hrec osa)=> [e' [c' [n' [ax2 ev n'B sv ]]]].
have ob: ordinalp a by apply os1; fprops.
move: (CNF_exp_add_expo ax2 ob n'B) => [e'' [c'' [n''[ax3 ew sw exv h1 h2]]]].
exists e'',c'',n''; rewrite ew - exv - sv ev.
split; [ exact | by rewrite setU2_A | exact | reflexivity | ].
move => i ia; move: (h2 _ ia) => [j [jb -> ->]].
by move: (h1 _ jb) => [k [kc -> ->]]; exists k; split.
Qed.

Lemma CNFq_exponents_r b e c n (e0 := e n) (E := (CNF_exponents e (succ n))):
   CNFq_ax b e c (succ n) -> inc n Bnat ->
   [/\ inc e0 E,
     (forall a, inc a E -> a <=o e0) &
     (CNF_exponents e n) = E -s1 e0].
Proof.
move=> ax nB; move: (BS_succ nB) => snB.
have ha:= (card_lt_succ nB).
have p1: inc e0 E by apply/(CNF_exponentsP _ snB);exists n;fprops.
have mono:= (CNF_exponents_M snB ax).
have pb: (forall a, inc a E -> a <=o e0).
  move => a /(CNF_exponentsP _ (BS_succ nB)) [j /(card_lt_succ_leP nB) jl ->].
  by apply: (CNF_exponents_M snB ax _ ha).
split => //; set_extens t.
  move /(CNF_exponentsP _ nB) => [i lin ->]; apply /setC1_P; split.
    apply/(CNF_exponentsP _ snB); exists i => //; co_tac.
  exact: (proj2 ((CNF_exponents_sM snB ax) i n lin (card_lt_succ nB))).
case/setC1_P => /(CNF_exponentsP _ snB) [i lin ->] ns.
apply/(CNF_exponentsP _ nB); exists i => //; split.
 by move/(card_lt_succ_leP nB): lin.
by dneg h; rewrite h.
Qed.

Lemma CNFq_extensionality1 b ex cx n ey cy m:
  inc n Bnat -> inc m Bnat -> CNFq_ax b ex cx n -> CNFq_ax b ey cy m ->
  CNF_exponents ex n = CNF_exponents ey m ->
  (n = m /\ forall i, i <c n -> ex i = ey i).
Proof.
move => nB; move:n nB m; apply: cardinal_c_induction.
  move => m mB ax1 ax2 eq1.
  have zm: \0c = m.
    by rewrite (CNF_exponents_card ax1 BS0) eq1 - (CNF_exponents_card ax2 mB).
  by split => // i /card_lt0.
move => n nB Hrec m mB ax1 ax2 sv.
have snB:= BS_succ nB.
have sm: succ n = m.
  by rewrite (CNF_exponents_card ax1 snB) (CNF_exponents_card ax2 mB) sv.
split; first by exact.
rewrite - sm in ax2 sv.
move: (CNFq_exponents_r ax1 nB) => [sa sb sc].
move:(CNFq_exponents_r ax2 nB) => [sa' sb' sc'].
rewrite - sv in sa' sb' sc'.
have ha: ex n = ey n by move: (sb _ sa')(sb' _ sa) => h1 h2; ord_tac.
have hn: CNF_exponents ex n = CNF_exponents ey n by rewrite sc sc' ha.
clear sa sb sc sa' sb' sc'.
move: (proj2 (Hrec _ nB (CNFq_p5 nB ax1) (CNFq_p5 nB ax2) hn)) => sa.
move => i /(card_lt_succ_leP nB) lin; case: (equal_or_not i n) => ein.
  by rewrite ein ha.
by apply: sa; split.
Qed.

Lemma CNFq_zero b e c n:
   inc n Bnat -> CNFq_ax b e c n -> CNFbv b e c n = \0o ->
   forall i, i <c n -> c i = \0o.
Proof.
move: n; apply: cardinal_c_induction; first by move => _ _ i /card_lt0.
move => n nB Hrec ax; rewrite (CNFq_p1 _ _ _ nB) => eq.
have o1:=(CNFq_p0 ax (card_lt_succ nB)).
have o2 :=(OS_CNFq nB (CNFq_p5 nB ax)).
move: (osum2_zero o1 o2 eq) => [eq1 eq2] i lin; case:(equal_or_not i n) => h.
  move: (ax) => [_ a1 a2 _].
  have oc:=(proj31_1 (a2 _ (card_lt_succ nB))).
  rewrite h;case: (ord_zero_dichot oc) => // cp.
  move: (opow_pos (CNFq_pg0 ax) (a1 _ (card_lt_succ nB))) => h'.
  by move: (proj2 (oprod2_pos h' cp)); rewrite -/(cantor_mon b e c n) eq1.
apply: (Hrec (CNFq_p5 nB ax) eq2); split => //.
by move/(card_lt_succ_leP nB): lin.
Qed.

Lemma CNFq_extensionality b ex cx n ey cy m :
  inc n Bnat -> inc m Bnat -> CNFq_ax b ex cx n -> CNFq_ax b ey cy m ->
  CNFbv b ex cx n = CNFbv b ey cy m ->
  CNF_exponents ex n = CNF_exponents ey m ->
  (n = m /\ forall i, i <c n -> ex i = ey i /\ cx i = cy i).
Proof.
move => nB; move: n nB m; apply: cardinal_c_induction.
  move => m mB ax ay sv se.
  move: (CNFq_extensionality1 BS0 mB ax ay se) => [mz _].
  split; [ exact | move => i /card_lt0; case].
move => n nB Hrec m mB ax ay sv se.
have nn:= (card_lt_succ nB).
move: (CNFq_extensionality1 (BS_succ nB) mB ax ay se) => [mn eq1].
split; first by exact.
rewrite -mn in sv ay.
have se1: ex n = ey n by apply: (eq1 _ nn).
move: (ax)(ay)=> [_ a1 a2 _][_ _ a3 _].
move: (a1 _ nn)(proj31_1 (a2 _ nn))(a3 _ nn)=> oexn ocxn [[ocyn ob _] _].
move: (OS_pow ob oexn) => o1.
move: (OS_prod2 o1 ocxn)(OS_prod2 o1 ocyn) => o2 o3.
move: sv.
rewrite (CNFq_p1 _ _ _ nB) (CNFq_p1 _ _ _ nB) /cantor_mon - se1.
move: (CNFq_pg nB ax) (CNFq_pg nB ay); rewrite - se1 => lt1 lt2 eq3.
move:(proj31_1 lt2) (proj31_1 lt1)=> o4 o5.
move: (osum_Meqlt lt1 o2); rewrite - (oprod2_succ o1 ocxn).
move: (osum_Meqlt lt2 o3); rewrite - (oprod2_succ o1 ocyn).
move: (osum_Mle0 o3 o4) (osum_Mle0 o2 o5);rewrite eq3 => lta ltb ltc ltd.
move: (ord_le_ltT lta ltd) (ord_le_ltT ltb ltc) => la lb.
move: (oprod_Meqltr ocyn (OS_succ ocxn) o1 la) => /ord_lt_succP.
move: (oprod_Meqltr ocxn (OS_succ ocyn) o1 lb) => /ord_lt_succP lc ld.
move: (ord_leA lc ld) => e1.
have ha:CNF_exponents ex n = CNF_exponents ey n.
   rewrite (proj33 (CNFq_exponents_r ax nB))(proj33 (CNFq_exponents_r ay nB)).
   by rewrite - se1 se mn.
rewrite - e1 in eq3.
move: (osum2_simpl o5 o4 o2 eq3) => eq2.
move: (Hrec n nB (CNFq_p5 nB ax) (CNFq_p5 nB ay) eq2 ha) => [_] hb.
move => i lin; case: (equal_or_not i n) => ein.
    by rewrite ein e1 se1; split.
apply: hb; split; [ by move/(card_lt_succ_leP nB): lin | exact].
Qed.

Definition CNFQ_bound b I ne:=
   Bnat \times ((gfunctions I ne) \times (gfunctions I b)).

Definition CNFq_extension b e c n e' :=
  let ne := (CNF_exponents e n) \cup e' in
   select (fun X => [/\ CNFq_ax b (Vg (P (Q X))) (Vg (Q (Q X))) (P X),
      CNFbv b (Vg (P (Q X))) (Vg (Q (Q X))) (P X) = CNFbv b e c n,
      CNF_exponents (Vg (P (Q X))) (P X) = ne &
      CNF_ext_coeffs e c n (Vg (P (Q X))) (Vg (Q (Q X))) (P X)])
    (CNFQ_bound b (Bint (cardinal ne)) ne).

Lemma CNFq_extension_pr b e c n A (B :=(CNF_exponents e n) \cup A)
   (Y := CNFq_extension b e c n A)
   (m := P Y) (e' := Vg (P (Q Y))) (c' := Vg (Q (Q Y))):
  CNFq_ax b e c n -> inc n Bnat -> finite_set A -> ordinal_set A ->
  [/\ CNFq_ax b e' c' m, CNF_exponents e' m = B, inc m Bnat &
     [/\ domain (P (Q Y)) = Bint m,
      domain (Q (Q Y)) = Bint m,
      CNFbv b e c n = CNFbv b e' c' m & CNF_ext_coeffs e c n e' c' m]].
Proof.
move => ha hb hc hd.
set E := (CNFQ_bound b (Bint (cardinal B)) B).
pose p := (fun X => [/\ CNFq_ax b (Vg (P (Q X))) (Vg (Q (Q X))) (P X),
      CNFbv b (Vg (P (Q X))) (Vg (Q (Q X))) (P X) = CNFbv b e c n,
      CNF_exponents (Vg (P (Q X))) (P X) = B &
      CNF_ext_coeffs e c n (Vg (P (Q X))) (Vg (Q (Q X))) (P X)]).
have h: singl_val2 (inc^~ E) p.
  move => X X' /= /setX_P [sa sb /setX_P [sc sd se]] [sf sg sh si].
  move => /setX_P [sa' sb' /setX_P [sc' sd' se']] [sf' sg' sh' si'].
  move:(CNF_exponents_card sf sb); rewrite sh => sj.
  move:(CNF_exponents_card sf' sb'); rewrite sh' => sj'.
  rewrite - sj in sd se; rewrite - sj' in sd' se'.
  rewrite - sg' in sg; rewrite - sh' in sh.
  move: (CNFq_extensionality sb sb' sf sf' sg sh) => [sa'' sb''].
  have eq1: P (Q X) = P (Q X').
    move/ graphset_P2: sd => [u1 u2 u3].
    move/ graphset_P2: sd' => [u1' u2' u3'].
    apply:fgraph_exten; [exact | exact | by rewrite u2 u2' sa''| move => i id].
    by rewrite u2 in id; move/(BintP sb): id => /sb'' [].
  have eq2: Q (Q X) = Q (Q X').
    move/ graphset_P2: se => [u1 u2 u3].
    move/ graphset_P2: se' => [u1' u2' u3'].
    apply:fgraph_exten; [exact | exact | by rewrite u2 u2' sa''| move => i id].
    by rewrite u2 in id; move/(BintP sb): id => /sb'' [].
  have qq: Q X = Q X' by apply: pair_exten.
  by apply: pair_exten.
move: (CNF_exp_add_expos ha hb hc hd) => [e'' [c'' [n'' [a1 a2 a3 a4 a5]]]].
set X := J n'' (J (Lg (Bint n'') e'') (Lg (Bint n'') c'')).
have pX: p X.
  have s1: same_below e'' (Vg (P (Q X))) n''.
    by move => i lin; rewrite /X; aw; bw; move/(BintP a3):lin.
  have s2: same_below c'' (Vg (Q (Q X))) n''.
    by move => i lin; rewrite /X; aw; bw; move/(BintP a3):lin.
  have nn: P X = n'' by rewrite /X; aw.
  move: (CNFq_exten s1 s2 a1 a3)=> [sa sb].
  have cc :CNF_exponents (Vg (P (Q X))) n'' = B.
    by rewrite /B -a2; set_extens t; move /(CNF_exponentsP _ a3) => [i lin ->];
    apply /(CNF_exponentsP _ a3); exists i; try (exact lin); rewrite (s1 _ lin).
  have ce:CNF_ext_coeffs e c n (Vg (P (Q X))) (Vg (Q (Q X))) n''.
   move => i lin;move :(a5 _ lin) => [j [lj -> ->]].
   by rewrite (s1 _ lj) (s2 _ lj); exists j; split.
  hnf; rewrite nn - sb a4; split; (try reflexivity); exact.
move:(CNF_exponents_card a1 a3); rewrite a2 -/B => eq1.
have XE: inc X E.
  have p1: fgraph (Lg (Bint n'') e'') by fprops.
  have p2: fgraph (Lg (Bint n'') c'') by fprops.
  rewrite /X;apply/setX_P; split; [fprops | aw | aw ].
  apply/setX_P; split;fprops; apply/graphset_P2;rewrite ?pr1_pair ?pr2_pair; bw.
  split; [ exact | by rewrite eq1 | move => i /(range_gP p1)].
    bw; move => [x xd ->]; bw; rewrite /B - a2; apply/(CNF_exponentsP _ a3).
    exists x; [ by apply/(BintP a3) | exact].
  split; [ exact | by rewrite eq1 | move => i /(range_gP p2)].
    bw; move => [x xd ->]; bw; move/(BintP a3): xd => xn.
    by move: (a1) => [a7 _ a6 _]; move/(ord_ltP (proj32 a7)):(a6 _ xn).
move: (select_uniq h XE pX) => xy.
have XY: X = Y by rewrite xy ; reflexivity.
have -> : m = n'' by rewrite /m - XY /X; aw.
have s1: same_below e'' e' n''.
  by move => i lin; rewrite /e' - XY /X; aw; bw; move/(BintP a3):lin.
have s2: same_below c'' c' n''.
  by move => i lin; rewrite /c' - XY /X; aw; bw; move/(BintP a3):lin.
have cc: CNF_exponents e' n'' = B.
 rewrite /B -a2; symmetry; apply: (CNF_exponents_exten a3 s1).
have cd:CNF_ext_coeffs e c n e' c' n''.
  move => i lin; move :(a5 _ lin) => [j [lj -> ->]].
  by rewrite (s1 _ lj)(s2 _ lj); exists j; split.
move: (CNFq_exten s1 s2 a1 a3) => [sa <-].
move: XE; rewrite XY => /setX_P [_ _ /setX_P [_ /graphset_P2 [_ -> _]]].
move => /graphset_P2 [_ -> _]; rewrite - eq1.
by split;last split.
Qed.

Lemma CNFq_extension2_pr b ex cx n ey cy m
   (Ex := CNF_exponents ex n) (Ey := CNF_exponents ey m)
   (X := CNFq_extension b ex cx n Ey)
   (Y := CNFq_extension b ey cy m Ex)
   (n' := P X) (ex' := Vg (P (Q X))) (cx' := Vg (Q (Q X)))
   (m' := P Y) (ey' := Vg (P (Q Y))) (cy' := Vg (Q (Q Y))):
  inc n Bnat -> inc m Bnat -> CNFq_ax b ex cx n -> CNFq_ax b ey cy m ->
  [/\ CNFq_ax b ex' cx' n', CNFq_ax b ey' cy' m',
      CNFbv b ex cx n = CNFbv b ex' cx' n',
      CNFbv b ey cy m = CNFbv b ey' cy' m'&
   [/\ n' = m', inc n' Bnat, domain (Q (Q X)) = Bint n',
     domain (Q (Q Y)) = Bint n' &
    [/\ same_below ex' ey' n',
     CNF_exponents ex' n' = Ex \cup Ey,
     CNF_ext_coeffs ex cx n ex' cx' n' & CNF_ext_coeffs ey cy m ey' cy' m']]].
Proof.
move => nB mB pa pb.
move :(CNF_exponents_of pa nB) (CNF_exponents_of pb mB) => [qa qb] [qc qd].
move: (CNFq_extension_pr pa nB qc qd); rewrite -/X -/ex' -/cx' -/n'.
move: (CNFq_extension_pr pb mB qa qb); rewrite -/Y -/ey' -/cy' -/m'.
move=> [ra rb m'B [d1 d2 rc rc']] [rd re n'B [d1' d2' rf rf']].
have ss: CNF_exponents ex' n' = CNF_exponents ey' m' by rewrite re setU2_C - rb.
move: (CNFq_extensionality1 n'B m'B rd ra ss) => [nn sv].
rewrite - nn in d2;rewrite d2.
split; last split; last split;exact.
Qed.

Lemma CNF_sum_ax ex cx n ey cy :
   CNFq_ax omega0 ex cx n -> CNFq_ax omega0 ey cy n -> inc n Bnat ->
   CNFq_ax omega0 ex (fun i => (cx i) +o (cy i)) n.
Proof.
move => [a1 a2 a3 a4] [_ b2 b3 b4]; split; try exact.
by move => i lin; apply: (osum2_lt_omega (a3 _ lin) (b3 _ lin)).
Qed.

Lemma CNF_sum_bounded1 ex cx n ey cy :
  CNFq_ax omega0 ex cx n -> CNFq_ax omega0 ey cy n -> inc n Bnat ->
  forall i, i<c n ->
   cx i <=o \osup (CNF_coefficients (fun i => (cx i) +o (cy i)) n).
Proof.
move => [_ _ ax _] [_ _ ay _] sd i idx.
have oq2:= (proj31_1 (ay _ idx)).
have oq1:= (proj31_1 (ax _ idx)).
apply: (ord_leT (osum_Mle0 oq1 oq2)).
apply: ord_sup_ub.
   move => k /(CNF_exponentsP _ sd) [j ljn ->].
   move: (proj31_1 (ay _ ljn)) (proj31_1 (ax _ ljn)) => ob oa.
   apply: (OS_sum2 oa ob).
by apply/(CNF_exponentsP _ sd); exists i.
Qed.

Lemma CNF_sup_coef_pr e c n (s := \osup (CNF_coefficients c n)) :
   inc n Bnat -> CNFq_ax omega0 e c n ->
   (s = \0c \/ exists2 i, i <c n & s = c i).
Proof.
move=> nB [_ _ h _].
have cs: ordinal_set (CNF_coefficients c n).
  move => t /funI_P [z /(BintP nB) /h ze ->]; ord_tac.
move: (wordering_ordinal_le_pr cs).
set r := (graph_on ordinal_le (CNF_coefficients c n)).
move => [qa qb]; move: (worder_total qa) => tor.
have fs: finite_set (substrate r).
  rewrite qb;apply:finite_fun_image; apply:finite_Bint.
case: (emptyset_dichot (substrate r)) => es.
  by left; rewrite /s -qb es setU_0.
move: (finite_set_torder_greatest tor fs es) => [x []].
rewrite qb; move => xr sm.
move: (xr) => /(CNF_exponentsP _ nB) [z zi zv].
right; exists z; [exact | ].
rewrite - zv; apply: ord_leA.
  apply: ord_ub_sup => //; first by apply: cs.
  by move=> i id; move: (sm _ id) => /graph_on_P1 [_ _].
apply: ord_sup_ub => //.
Qed.

Definition ord_natural_sum_aux (x y: Set):=
  let X := the_CNF x in let Y := the_CNF y in
  let ex := Vg (P (Q X)) in let ey := Vg (P (Q Y)) in
  let Ex := CNF_exponents ex (P X) in let Ey := CNF_exponents ey (P Y) in
  let xa := CNFq_extension omega0 ex (Vg (Q (Q X))) (P X) Ey in
  let ya := CNFq_extension omega0 ey (Vg (Q (Q Y))) (P Y) Ex in
  let cxa := Q (Q xa) in let cya := Q (Q ya) in
  J (P xa)
    (J (P (Q xa))
     (Lg (domain (Q (Q xa))) (fun i : Set => Vg cxa i +o Vg cya i))).

Lemma natural_sum_p1 x y (s := ord_natural_sum_aux x y) (X:= the_CNF x)
  (n := P s) (e := Vg (P (Q s))) (c := Vg (Q (Q s))):
  ordinalp x -> ordinalp y ->
  [/\ inc n Bnat, CNFb_axo e c n &
    forall i, i <c (P X) ->
     Vg (Q (Q X)) i <=o \osup (CNF_coefficients (Vg (Q (Q s))) (P s)) ].
Proof.
move => ox oy.
move:(the_CNF_p0 ox) => [/CNFB_ax_simp].
move:(the_CNF_p0 oy) => [/CNFB_ax_simp].
set ex:= (Vg (P (Q (the_CNF x)))); set ey := (Vg (P (Q (the_CNF y)))).
set cx:= (Vg (Q (Q (the_CNF x)))); set cy := (Vg (Q (Q (the_CNF y)))).
set ny:= (P (the_CNF y)); set nx:= (P (the_CNF x)).
move => [nyB ay] yv [nxB ax] xv.
move: (CNFq_extension2_pr nxB nyB (proj1 ax) (proj1 ay)).
rewrite /e/c/n/s /ord_natural_sum_aux.
rewrite !(pr1_pair, pr2_pair).
set nz:= P _.
set X1 := CNFq_extension _ _ _ _ _.
set Y1 := CNFq_extension _ _ _ _ _.
set n1 := P X1; set m1:= P Y1.
set e1 :=Vg (P (Q X1)); set e2:=Vg (P (Q Y1)).
set c1 :=Vg (Q (Q X1)); set c2:=Vg (Q (Q Y1)).
move => [ax1 ax2 eq1 eq2 [nm1 n1B d1 _ [eq3 eq4 s1 s2]]].
rewrite - nm1 in eq2 ax2.
move:(CNF_sum_bounded1 ax1 ax2 n1B).
set x1 := CNF_exponents _ _; set x2 := CNF_exponents _ _ => ha.
have <- : x1 = x2.
  apply: (CNF_exponents_exten n1B) => i lin; bw; rewrite d1.
  by apply /(BintP n1B).
split; [ exact | |by move => i inx;move: (s1 _ inx) => [j [/ha ja _ ->]] ].
split.
  move: (CNF_sum_ax ax1 ax2 n1B);apply:CNFq_ax_exten => i lein; first exact.
  by bw; rewrite d1; apply /(BintP n1B).
move => i lin1; bw; last by rewrite d1; apply /(BintP n1B).
have oc1: ordinalp (c1 i) by exact (proj31 (ha _ lin1)).
have oc2: ordinalp (c2 i).
  by move:(ax2) => [_ _ h _]; exact:(proj31_1 (h _ lin1)).
apply: (ord_ne0_pos (OS_sum2 oc1 oc2)) => eq5.
move: (osum2_zero oc1 oc2 eq5) => [c1z c2z].
have: inc (e1 i) (CNF_exponents e1 n1).
   by apply/(CNF_exponentsP _ n1B); exists i.
rewrite eq4; case /setU2_P.
  move/(CNF_exponentsP _ nxB) => [j ljn eq7].
  move: (s1 j ljn) => [k [km va vb]]; rewrite - eq7 in va.
  move: ax => [_ h]; move: (proj2 (h _ ljn)); rewrite vb.
  by rewrite - (CNF_exponents_I n1B ax1 lin1 km va) c1z; case.
have ->: e1 i = e2 i by apply: eq3.
move/(CNF_exponentsP _ nyB) => [j ljn eq7].
move: (s2 j ljn) => [k [km va vb]]; rewrite - eq7 in va.
move: ay => [_ h]; move: (proj2 (h _ ljn)); rewrite vb.
rewrite - nm1 in km.
by rewrite - (CNF_exponents_I n1B ax2 lin1 km va) c2z; case.
Qed.

Definition ord_natural_sum x y:=
  let s:= ord_natural_sum_aux x y in
  CNFbvo (Vg (P (Q s))) (Vg (Q (Q s))) (P s).

Notation "x +#o y" := (ord_natural_sum x y) (at level 50).

Lemma OS_natural_sum x y :
  ordinalp x -> ordinalp y -> ordinalp (x +#o y).
Proof.
move => ox oy; move: (natural_sum_p1 ox oy) => [sa sb sc].
exact:(OS_CNFb sa sb).
Qed.

Lemma natural_sum_p2 x y (s := the_CNF (x +#o y))
   (ex := Vg (P (Q (the_CNF x)))) (cx := Vg (Q (Q (the_CNF x))))
   (es:= Vg (P (Q s))) (cs:= Vg (Q (Q s))) (n:= P (the_CNF x))(m:= P s):
   ordinalp x -> ordinalp y ->
    (sub (CNF_exponents ex n) (CNF_exponents es m)
     /\ (forall i, i <c n -> cx i <=o \osup (CNF_coefficients cs m))).
Proof.
move => ox oy; move: (erefl m); rewrite {1} /m /cs /es /s.
move: (natural_sum_p1 ox oy) => [].
move:(the_CNF_p0 ox) => [/CNFB_ax_simp].
move:(the_CNF_p0 oy) => [/CNFB_ax_simp]; rewrite -/ex -/cx.
set ey := (Vg (P (Q (the_CNF y)))); set cy := (Vg (Q (Q (the_CNF y)))).
rewrite -/n ; move=>[nyB ay] _ [nB ax] _.
rewrite /ord_natural_sum /ord_natural_sum_aux !(pr1_pair, pr2_pair).
set X := (the_CNF x) ; set Y := (the_CNF y); rewrite -/n.
rewrite -/ex -/cx -/ey -/cy.
set X' := (CNFq_extension omega0 _ _ _ _);
set Y' := (CNFq_extension omega0 _ _ _ _).
set Z := the_CNF _ => pa pb pc pd.
move: (the_CNF_p3 pb pa). rewrite -/Z /CNFbB.
set ez:= Lg _ _; set cz := Lg _ _ => eq1.
move: (f_equal P eq1);rewrite pr1_pair pd => eq0.
move: (f_equal Q eq1);rewrite pr2_pair => eq2.
move: (f_equal P eq2) (f_equal Q eq2); rewrite pr2_pair pr1_pair => -> ->.
move: (CNFq_extension2_pr nB nyB (proj1 ax) (proj1 ay)).
rewrite -/Y' -/X'; move => [_ _ _ _[ _ _ dy _ [_ eqa _ _]]].
split.
  have ssae: same_below (Vg (P (Q X'))) (Vg ez) (P X').
    by move => i lin; move: (BS_lt_int lin pa) => iB; rewrite /ez; bw;Ytac0.
  rewrite eq0 -(CNF_exponents_exten pa ssae) eqa;apply:subsetU2l.
have ssac: same_below (Vg (Lg (domain (Q (Q X')))
         (fun k => Vg (Q (Q X')) k +o Vg (Q (Q Y')) k))) (Vg cz) (P X').
  move => i lin; rewrite /cz; move: (BS_lt_int lin pa) => iB.
  have id: inc i (domain (Q (Q X'))) by rewrite dy; apply /(BintP pa).
  by bw; Ytac0.
by move: pc; rewrite (CNF_exponents_exten pa ssac) eq0.
Qed.

Lemma ord_natural_sum0 x : ordinalp x -> (x +#o \0o) = x.
Proof.
move => ox.
move:(the_CNF_p0 ox) => [/CNFB_ax_simp].
move:(the_CNF_p0 OS0) => [/CNFB_ax_simp].
move=> [nB ax xv][mB ay yv].
rewrite /ord_natural_sum /ord_natural_sum_aux !(pr1_pair, pr2_pair).
move: (CNFq_extension2_pr mB nB (proj1 ay) (proj1 ax)).
set Y:= CNFq_extension _ _ _ _ _.
set X:= CNFq_extension _ _ _ _ _.
move => [pa pb pc pd [pe pf pg _ _]].
rewrite - yv /CNFBv /CNFbvo pc pg.
apply: (osum_expansion_exten pf) => i lin.
have idx: inc i (Bint (P Y)) by apply /(BintP pf).
rewrite /cantor_mon; bw.
move: pa => [_ _ ha _]; move: (proj31_1 (ha _ lin)) => oc1.
move: xv; rewrite /CNFBv /CNFbvo pd => xv;rewrite pe in pf lin.
by rewrite(CNFq_zero pf pb xv lin) (osum0r oc1).
Qed.

Lemma ord_natural_sumC x y :
  ordinalp x -> ordinalp y -> (x +#o y) = (y +#o x).
Proof.
move => ox oy.
move:(the_CNF_p0 ox) => [/CNFB_ax_simp].
move:(the_CNF_p0 oy) => [/CNFB_ax_simp].
move=> [nB ax xv][mB ay yv].
rewrite /ord_natural_sum /ord_natural_sum_aux !(pr1_pair, pr2_pair).
move: (CNFq_extension2_pr nB mB (proj1 ax) (proj1 ay)).
set X1:= CNFq_extension _ _ _ _ _.
set Y1:= CNFq_extension _ _ _ _ _.
move => [pa pb pc pd [pe pf pg w [ph _ _ _]]].
rewrite -pe; apply: (osum_expansion_exten pf) => i lin.
have ha: inc i (domain (Q (Q X1))) by rewrite pg; apply /(BintP pf).
rewrite w - pg /cantor_mon (ph _ lin) {1} (LVg_E _ ha) (LVg_E _ ha).
move: pa pb => [a1 a2 a3 _] [_ b2 b3 _]; rewrite - pe in b3.
move:(a3 _ lin) (b3 _ lin) => sa sb.
have ub: inc (Vg (Q (Q X1)) i) Bnat by apply /olt_omegaP.
have vb: inc (Vg (Q (Q Y1)) i) Bnat by apply /olt_omegaP.
by rewrite (osum2_2int ub vb)(osum2_2int vb ub) csumC.
Qed.

Lemma CNF_M0le b ex cx n ey cy :
  inc n Bnat -> CNFq_ax b ex cx n -> CNFq_ax b ey cy n ->
  (forall i, i <c n -> (ex i) = ey i /\ cx i <=o cy i)
 -> CNFbv b ex cx n <=o CNFbv b ey cy n.
Proof.
move: n; apply: cardinal_c_induction.
  move => _ _ _; rewrite CNFq_p2 CNFq_p2; apply: (ord_leR OS0).
move => n nB Hrec ax1 ax2 h.
rewrite (CNFq_p1 _ _ _ nB) (CNFq_p1 _ _ _ nB).
have h1: (forall i, i <c n -> ex i = ey i /\ cx i <=o cy i).
  move => i lin; exact (h _ (card_lt_leT lin (card_le_succ nB))).
move: (Hrec (CNFq_p5 nB ax1) (CNFq_p5 nB ax2) h1) => l1.
rewrite /cantor_mon;move: (h _ (card_lt_succ nB)) => [-> le2].
move: (ax2) => [[_ ob _] oen _ _].
exact:(osum_Mlele (oprod_Meqle le2 (OS_pow ob (oen _ (card_lt_succ nB)))) l1).
Qed.

Lemma ord_natural_small x y e :
  ordinalp e -> x <o (omega0 ^o e) -> y <o (omega0 ^o e) ->
  (x +#o y) <o (omega0 ^o e).
Proof.
move => oe qa qb.
have ox: (ordinalp x) by ord_tac.
have oy: (ordinalp y) by ord_tac.
move:(natural_sum_p1 ox oy).
rewrite /ord_natural_sum /ord_natural_sum_aux !(pr1_pair, pr2_pair).
set X:= CNFq_extension _ _ _ _ _.
set Y:= CNFq_extension _ _ _ _ _.
move => [sa sb _]; apply: (CNFq_pg3 sa (proj1 sb) oe) => i ip.
move: (CNFq_exists3 oe qa) (CNFq_exists3 oe qb) => qc qd.
move:(the_CNF_p0 ox) => [/CNFB_ax_simp [nB ax] xv].
move:(the_CNF_p0 oy) => [/CNFB_ax_simp [mB ay] yv].
move: (CNFq_extension2_pr nB mB (proj1 ax) (proj1 ay))
 => [_ _ _ _ [_ _ _ _ [_ e3 _ _]]].
have : inc (Vg (P (Q X)) i) (CNF_exponents (Vg (P (Q X))) (P X)).
  by apply /(CNF_exponentsP _ sa); exists i.
rewrite e3; case /setU2_P; fprops.
Qed.

Lemma ord_natural_M0le x y :
  ordinalp x -> ordinalp y -> x <=o (x +#o y).
Proof.
move => ox oy.
move:(natural_sum_p1 ox oy).
rewrite /ord_natural_sum /ord_natural_sum_aux !(pr1_pair, pr2_pair).
move => [sa sb _].
move:(the_CNF_p0 ox) => [/CNFB_ax_simp [nB ax] xv].
move:(the_CNF_p0 oy) => [/CNFB_ax_simp [mB ay] yv].
move: (CNFq_extension2_pr nB mB (proj1 ax) (proj1 ay)).
set X:= CNFq_extension _ _ _ _ _.
set Y:= CNFq_extension _ _ _ _ _.
move => [ax1 ax2 v1 _ [n1n2 n1B d1 _ [e1 e2 e3 e4]]].
rewrite -xv /CNFBv /CNFbvo v1.
apply:(CNF_M0le n1B ax1 (proj1 sb)); rewrite -/X -/Y.
move => i lin.
have id: inc i (domain (Q (Q X))) by rewrite d1; apply /(BintP n1B).
move: ax1 => [_ _ a1 _]; move: (proj31_1 (a1 _ lin)) => o1.
split; [exact | bw]; rewrite n1n2 in lin.
move: ax2 => [_ _ a2 _]; exact (osum_Mle0 o1 (proj31_1 (a2 _ lin))).
Qed.

Definition CNF_subset x:=
  let X := the_CNF x in let n:= P X in let e := Vg (P (Q X))
  in let c := Vg (Q (Q X))
  in let E1:=(CNF_exponents e n)
  in let E2:=(succ_o (\osup (CNF_coefficients c n)))
  in let G:= fun z => singleton z \times
    (gfunctions (Bint z) E1 \times gfunctions (Bint z) E2)
  in let G1 := unionf (Bintc n) G
  in fun_image G1 (fun z => CNFbvo (Vg (P (Q z))) (Vg (Q (Q z))) (P z)).

Lemma natural_sum_p3 x y (E :=CNF_subset (x +#o y)):
  ordinalp x -> ordinalp y ->
  (inc x E /\ finite_set E).
Proof.
move => ox oy.
have oz:=(OS_natural_sum ox oy).
split.
  rewrite /E /CNF_subset; apply /funI_P.
  move:(the_CNF_p0 ox) => [/CNFB_ax_simp [nB ax] xv].
  move:(the_CNF_p0 oy) => [/CNFB_ax_simp [mB [ay _]] yv].
  move: (CNFq_extension2_pr nB mB (proj1 ax) ay).
  set X:= CNFq_extension _ _ _ _ _.
  set Y:= CNFq_extension _ _ _ _ _.
  move => [ax1 ax2 v1 _ [n1n2 n1B d1 _ [e1 e2 e3 e4]]].
  set n :=(P (the_CNF x)).
  set e := (Lg (Bint n) (Vg (P (Q (the_CNF x))))).
  set c := (Lg (Bint n) (Vg (Q (Q (the_CNF x))))).
  have sa: same_below (Vg (P (Q (the_CNF x)))) (Vg e) n.
    by move => i /(BintP nB) lin; rewrite /e; bw.
  have sb: same_below (Vg (Q (Q (the_CNF x)))) (Vg c) n.
    move => i/(BintP nB) lin; rewrite /c; bw.
  move: (CNFb_exten sa sb ax nB) => [sc sd].
  exists (J n (J e c)); last first.
    symmetry; move: sd; rewrite -/e -/c !(pr1_pair, pr2_pair) /CNFbvo.
    move => <-; exact: xv.
  have ha: fgraph (Lg (Bint n) (Vg (P (Q (the_CNF x))))) by fprops.
  have hb: fgraph (Lg (Bint n) (Vg (Q (Q (the_CNF x))))) by fprops.
  move: (natural_sum_p2 ox oy) => [se sf].
  have nI: inc n (Bintc (P (the_CNF (x +#o y)))).
    move:(the_CNF_p0 oz) => [/CNFB_ax_simp [snB [az _]] zv].
    apply /(BintcP snB).
    rewrite /n (CNF_exponents_card (proj1 ax) nB) (CNF_exponents_card az snB).
    exact: (sub_smaller se).
  apply/setUf_P; exists n; [exact | apply /setX_P].
  split; [ fprops | apply /set1_P; aw | apply /setX_P].
  split; first by rewrite pr2_pair; apply: pair_is_pair.
    rewrite pr2_pair pr1_pair /e;apply /graphset_P2; split; [exact | bw |].
    rewrite /e => t /(range_gP ha); bw.
    move => [z zi ->]; bw;apply: se; apply/(CNF_exponentsP _ nB).
    exists z ; [by apply /(BintP nB)| exact].
  rewrite !pr2_pair /c; apply /graphset_P2; split; [ exact | bw | ].
  move => t /(range_gP hb); bw; move => [z zi ->]; bw.
  move /(BintP nB): zi => zlp.
  move: (sf _ zlp) => h; move/(ord_leP (proj32 h)): h; exact.
apply: finite_fun_image.
set I:= (Bintc (P (the_CNF (x +#o y)))).
set f := fun z: Set => _.
have ->: (unionf I f) = unionb (Lg I f).
  rewrite /unionb;bw; set_extens t => /setUf_P [u uI uv];
  apply/setUf_P; ex_tac; [ bw | move: uv; bw].
move:(the_CNF_p0 oz) => [/CNFB_ax_simp [mB ay] yv].
have: finite_set (domain (Lg I f)) by bw; apply:finite_Bintcc.
apply: finite_union_finite; hnf; bw => z zI; bw.
have Hb: forall A B,finite_set A -> finite_set B -> finite_set (gfunctions A B).
   move => A B /card_finite_setP ha /card_finite_setP hb.
   apply /card_finite_setP; move:(fun_set_equipotent A B) => /card_eqP <-.
   by rewrite cpow_pr1; fprops.
have fi: finite_set (Bint z) by apply:finite_Bint.
move: (proj1 (CNF_exponents_of (proj1 ay) mB)) => fs1.
move: (CNF_sup_coef_pr mB (proj1 ay)); set s := union _ => sp.
have sb: s <o omega0.
  case sp; first by move => ->; apply: ord_lt_0omega.
  by move => [i iln ->]; move: ay => [[_ _ h2 _] _]; apply:h2.
have sc:finite_set (succ_o s).
  move:omega_limit => /limit_ordinal_P3 [_ h].
  move: (h _ sb) => /olt_omegaP hh.
  by apply/card_finite_setP; rewrite (card_card (CS_Bnat hh)).
apply: finite_prod2; first by apply: set1_finite.
by apply: finite_prod2 ; apply: Hb.
Qed.

Lemma ord_natural_finite_cover x e (E:= omega0 ^o e):
  ordinalp e -> inc x E ->
  let n := cardinal (Zo (coarse E) (fun z => ord_natural_sum (P z) (Q z)= x)) in
  inc n Bnat /\ n <> \0c.
Proof.
move=> oe xE; set F := Zo _ _ => n.
have oE :=(OS_pow OS_omega oe).
have ox:= (ordinal_hi oE xE).
move: (proj2 (natural_sum_p3 ox OS0));rewrite (ord_natural_sum0 ox) => h.
move: (finite_prod2 h h); set G:= (CNF_subset x) => h1.
have h2: sub F (G \times G).
  move => t /Zo_P [/setX_P [pt Pt Qt] s].
  have op: ordinalp (P t) by ord_tac.
  have oq: ordinalp (Q t) by ord_tac.
  move:(proj1 (natural_sum_p3 op oq)) (proj1 (natural_sum_p3 oq op)).
  rewrite (ord_natural_sumC oq op) s -/G => sa sb.
  by apply/ setX_P.
split; first by move: (sub_finite_set h2 h1) => /card_finite_setP.
apply: cardinal_nonemptyset1.
have zE: inc \0o E.
   apply /(ord_ltP oE); split; first by apply: ozero_least.
   exact: (nesym(omega_pow_nz oe)).
exists (J x \0o); apply: Zo_i;first by apply :setXp_i.
by aw; apply: ord_natural_sum0.
Qed.


End Ordinals3.
Export Ordinals3.