Library sset11

Theory of Sets : Ordinals

Copyright INRIA (2009-2011) Apics Team (Jose Grimm).

Require Import ssreflect ssrbool eqtype ssrfun.
Set Implicit Arguments.
Unset Strict Implicit.
Import Prenex Implicits.
Require Export sset10.

Module Ordinals1.

Definition order_prod := lexicographic_order.
Definition order_prod_a := lexicographic_order_axioms.

Lemma order_prod_order: forall r g,
  order_prod_a r g -> order (order_prod r g).
Proof. move=> r g oa; apply lexorder_order => //. Qed.

Lemma order_prod_substrate: forall r g,
  order_prod_a r g ->
  substrate(order_prod r g) = prod_of_substrates g.
Proof. rewrite /order_prod_a/order_prod; move=> r g oa; bw. Qed.

Lemma prod_of_substrates_pr: forall i z g,
  inc i (domain g) -> inc z (prod_of_substrates g) ->
  inc (V i z) (substrate (V i g)).
Proof.
move=> i z g idg; rewrite /prod_of_substrates /fam_of_substrates; aw.
  bw; move=> [fgz [dz iVV]]; rewrite dz in iVV; move: (iVV _ idg); bw.
gprops.
Qed.

Lemma order_prod_gle: forall r g x x',
  order_prod_a r g ->
  gle (order_prod r g) x x' =
  (inc x (prod_of_substrates g) & inc x' (prod_of_substrates g) &
    (x= x' \/ exists j, inc j (substrate r) &
      glt (V j g) (V j x) (V j x') &
      forall i, glt r i j -> V i x = V i x')).
Proof.
move=> r g x x' la; rewrite /gle /order_prod lexorder_gle //.
have He: domain (fam_of_substrates g) = domain g.
  by rewrite /fam_of_substrates; bw.
have Hf: fgraph(fam_of_substrates g) by rewrite /fam_of_substrates; gprops.
apply iff_eq; move=> [xp [x'p aux]]; ee.
  set (s := (Zo (domain g) (fun i : Set => V i x <> V i x'))) in aux.
  have ssr: sub s (substrate r).
    by move: la => [_ [dg _]]; rewrite dg; apply Z_sub.
  case (emptyset_dichot s)=> nse.
    left; apply (productb_exten Hf xp x'p).
    move=> i idf; ex_middle h; empty_tac1 i.
    by rewrite /s; apply Z_inc; move: idf; rewrite /fam_of_substrates; bw.
    move: (la) => [[or wor] _]; move: (wor _ ssr nse)=> [j jle].
    move: (aux _ jle) => jp.
    move: jle => []; aw;move=> js jmin.
    have jsr: inc j (substrate r) by apply ssr.
    have dg: substrate r = domain g by move: la => [_ [res _]].
    right; ex_tac; ee; move=> i ij; ex_middle v.
    have iis : (inc i s) by rewrite /s; apply Z_inc =>//;rewrite -dg;order_tac.
    move: (iorder_gle1 (jmin _ iis)) => aux'; order_tac.
move: la => [wor [sr [fgg alo]]].
have Ha : order r by move: wor => [ok _].
set (s := (Zo (domain g) (fun i => V i x <> V i x'))).
have ssr: (sub s (substrate r)) by rewrite sr; apply Z_sub.
move=> j []; aw; move => js jmin.
move: js;rewrite /s Z_rw; move => [jdg Vj].
case aux => haux.
  rewrite haux in Vj;move: Vj; intuition.
move: haux => [i [isr [lti aeq]]].
have iis: inc i s by move:lti => [_ ok];rewrite /s; apply Z_inc =>//; ue.
move: (iorder_gle1 (jmin _ iis)) => ij.
case (equal_or_not j i) => ji; first by ue.
have ltji: glt r j i by split.
by elim Vj; apply aeq.
Qed.

Definition sum_of_substrates g := disjoint_union (fam_of_substrates g).

Lemma du_index_pr1: forall g x, inc x (sum_of_substrates g) ->
  (inc (Q x) (domain g) & inc (P x) (substrate (V (Q x) g)) & is_pair x).
Proof.
move=> g x; rewrite /sum_of_substrates /fam_of_substrates.
move=> xdu; move: (du_index_pr xdu); rewrite L_domain.
move=> [qd];bw; ee.
Qed.

Lemma inc_disjoint_union1: forall g x y,
  inc y (domain g) -> inc x (substrate (V y g)) ->
  inc (J x y) (sum_of_substrates g).
Proof.
move=> g x y ydg xsv; rewrite /sum_of_substrates/fam_of_substrates.
apply inc_disjoint_union; bw.
Qed.

Definition order_sum_a r g:=
  order r & substrate r = domain g & fgraph g &
  (forall i, inc i (domain g) -> order (V i g)).

Definition order_sum_r r g x x' :=
    (glt r (Q x) (Q x') \/ (Q x = Q x' & gle (V (Q x) g) (P x) (P x'))).

Definition order_sum r g :=
  graph_on (order_sum_r r g) (sum_of_substrates g).

Lemma order_sum_order: forall r g,
  order_sum_a r g -> order (order_sum r g).
Proof.
move=> r g [or [sr [fgg alo]]].
rewrite /order_sum.
set (s := order_sum_r r g).
set (x:= sum_of_substrates g).
have ->: (graph_on s x = graph_on (fun a b => inc a x & inc b x & s a b) x).
  rewrite /graph_on; set_extens t; rewrite Z_rw Z_rw; last by intuition.
  aw;move=> [tp sp]; ee.
apply order_from_rel;red; ee.
      rewrite /s /order_sum_r;move=> u v w [ux [vx suv]] [_ [xw suw]]; ee.
      case suv; case suw.
            left; order_tac.
          move=> [sq leq]; rewrite sq; intuition.
        move=> h [sq leq]; rewrite sq; intuition.
      move=> [sq1 le1][sq2 le2]; right; split; first by ue.
     move: (du_index_pr1 ux) => [qdg _].
     rewrite -sq2 in le1; move: (alo _ qdg)=> ov; order_tac.
   rewrite /s /order_sum_r;move=> u v [ux [vx suv]] [_ [_ svu]]; ee.
   move: (du_index_pr1 ux) => [qudg [puv pu]].
   move: (du_index_pr1 vx) => [qvdg [pvv pv]].
   case suv; case svu.
        move=> lt1 lt2; order_tac.
      move => [eq _] [_ lt1]; elim lt1; intuition.
    move => [_ lt1] [eq _]; elim lt1; intuition.
  move=> [eq le1][_ le2]; rewrite eq in le1; move: (alo _ qudg) => ou.
  apply pair_exten =>//;order_tac.
rewrite /s /order_sum_r; move=> u v [ux [vx suv]].
move: (du_index_pr1 ux) => [qudg [puv pu]].
move: (du_index_pr1 vx) => [qvdg [pvv pv]].
ee; right; ee.
  move: (alo _ qudg) => ou; order_tac.
move: (alo _ qvdg) => ou; order_tac.
Qed.

Lemma order_sum_substrate: forall r g, order_sum_a r g ->
  substrate(order_sum r g) = (sum_of_substrates g).
Proof.
move=> r g oa; rewrite /order_sum substrate_graph_on //.
move => a asg;rewrite /order_sum_r.
move: (du_index_pr1 asg) => [qudg [puv pu]].
right; ee.
move: oa =>[_[_[_ alo]]]; move: (alo _ qudg) => ou; order_tac.
Qed.

Lemma order_sum_gle: forall r g , order_sum_a r g ->
  forall x x', gle (order_sum r g) x x' =
  (inc x (sum_of_substrates g) & inc x' (sum_of_substrates g) &
    order_sum_r r g x x').
Proof. move=> r g oa x x'; rewrite /gle/order_sum graph_on_rw1 //. Qed.

Hint Resolve order_sum_order order_prod_order: fprops.

Lemma order_sum_gle1: forall r g x x',
  order_sum_a r g -> gle (order_sum r g) x x' ->
  (glt r (Q x) (Q x') \/ (Q x = Q x' & gle (V (Q x) g) (P x) (P x'))).
Proof. move=> r g x x' oa; rewrite order_sum_gle; ee. Qed.

Lemma order_sum_gle2: forall r g a b a' b',
  order_sum_a r g -> gle (order_sum r g) (J a b)(J a' b') ->
  (glt r b b' \/ (b = b' & gle (V b g) a a')).
Proof. move=> r g a b a' b' h1 h2; move:(order_sum_gle1 h1 h2); aw. Qed.

Lemma order_sum_gle_id: forall r g x x', order_sum_a r g ->
  gle (order_sum r g) x x' -> gle r (Q x) (Q x').
Proof.
move=> r g x x' oa; rewrite order_sum_gle//; move=> [_ [xs h]]; case h.
  by move=>[ok _].
move=> [eq _]; rewrite eq.
move: (du_index_pr1 xs)=> [qd _].
move: oa => [or [srdg _]]; order_tac; ue.
Qed.

Hint Rewrite order_sum_substrate order_prod_substrate : bw.

Definition order_prod2 r r' :=
  order_prod canonical_doubleton_order (variantLc r' r).

Definition order_sum2 r r' :=
  order_sum canonical_doubleton_order (variantLc r r').

Lemma order_sum2_axioms: forall r r', order r -> order r' ->
  order_sum_a canonical_doubleton_order (variantLc r r').
Proof.
move=> r r' or or'; red; ee.
by move: worder_canonical_doubleton_order => [ord _].
rewrite substrate_canonical_doubleton_order; bw.
bw; move=> i itp; try_lvariant itp.
Qed.

Lemma order_prod2_axioms: forall r r', order r -> order r' ->
  order_prod_a canonical_doubleton_order (variantLc r' r).
Proof.
move=> r r' or or'; red; red;ee.
by apply worder_canonical_doubleton_order.
rewrite substrate_canonical_doubleton_order; bw.
bw; move=> i itp; try_lvariant itp.
Qed.

Lemma order_sum2_order: forall r r', order r -> order r' ->
  order (order_sum2 r r').
Proof.
move=> r r' or or'; apply order_sum_order; apply order_sum2_axioms =>//.
Qed.

Lemma order_prod2_order: forall r r', order r -> order r' ->
  order (order_prod2 r r').
Proof.
move=> r r' or or'; apply lexorder_order; apply order_prod2_axioms =>//.
Qed.

Hint Resolve order_sum2_order order_prod2_order: fprops.

Lemma canonical2_substrate: forall r r',
  fam_of_substrates (variantLc r r') = variantLc (substrate r) (substrate r').
Proof.
move=> r r'; rewrite /fam_of_substrates /variantLc.
 apply fgraph_exten; gprops; fprops; bw.
move=> x xtp; try_lvariant xtp; fprops.
Qed.

Definition canonical_du2 a b :=
  disjoint_union (variantLc a b).

Lemma canonical_du2_rw: forall a b,
  canonical_du2 a b =
  union2 (product a (singleton TPa)) (product b (singleton TPb)).
Proof. by move=> a b ; rewrite /canonical_du2 disjoint_union2_rw1. Qed.

Lemma canonical_du2_pr: forall a b x,
  inc x (canonical_du2 a b) = (is_pair x &
  ((inc (P x) a & Q x = TPa) \/ (inc (P x) b & Q x = TPb))).
Proof.
move=> a b x; rewrite canonical_du2_rw; apply iff_eq; aw; intuition.
Qed.

Lemma canonical_du2_pr1: forall a b x,
  inc x (canonical_du2 a b) ->
  ((inc (P x) a & Q x = TPa) \/ (inc (P x) b & Q x = TPb)).
Proof.
by move=> a b x; rewrite canonical_du2_pr; move=> [_].
Qed.

Lemma canonical_du2_pr2: forall a b x,
  inc x (canonical_du2 a b) -> (Q x = TPa \/ Q x = TPb).
Proof.
by move=> a b x xc; case (canonical_du2_pr1 xc); move=> [_]; auto.
Qed.

Lemma canonical_du2_pra: forall a b x,
  inc x a -> inc (J x TPa) (canonical_du2 a b).
Proof. move=> a b x xa; rewrite canonical_du2_pr; ee; left; aw;ee. Qed.

Lemma canonical_du2_prb: forall a b x,
  inc x b -> inc (J x TPb) (canonical_du2 a b).
Proof. move=> a b x xa; rewrite canonical_du2_pr; ee; right; aw;ee. Qed.

Lemma order_sum2_substrate: forall r r', order r -> order r' ->
  substrate (order_sum2 r r') = canonical_du2 (substrate r) (substrate r').
Proof.
move=> r r' or or'; rewrite /canonical_du2 /order_sum2 order_sum_substrate.
  rewrite / sum_of_substrates canonical2_substrate //.
apply (order_sum2_axioms or or').
Qed.

Lemma order_prod2_substrate: forall r r',
  order r -> order r' ->
  substrate (order_prod2 r r') = product2 (substrate r') (substrate r).
Proof.
move=> r r' or or'; rewrite /order_prod2 lexorder_substrate.
 rewrite /prod_of_substrates canonical2_substrate /product2 /productf.
  rewrite /variantLc /variantL two_points_pr2 //.
apply order_prod2_axioms =>//.
Qed.

Lemma order_sum2_gle: forall r r' x x', order r -> order r' ->
  gle (order_sum2 r r') x x' =
  (inc x (canonical_du2 (substrate r) (substrate r')) &
    inc x' (canonical_du2 (substrate r) (substrate r')) &
    ((Q x = TPa & Q x' = TPa & gle r (P x) (P x'))
      \/ (Q x <> TPa & Q x' <> TPa & gle r' (P x) (P x'))
      \/ (Q x = TPa & Q x' <> TPa))).
Proof.
move=> r r' x x' or or'.
have Ha: TPb <> TPa by fprops.
have oa: order_sum_a canonical_doubleton_order (variantLc r r').
  by apply order_sum2_axioms.
set S1:= (canonical_du2 (substrate r) (substrate r')).
have S1p: S1 = (sum_of_substrates (variantLc r r')).
  by rewrite /sum_of_substrates canonical2_substrate.
rewrite /order_sum2; apply iff_eq; rewrite (order_sum_gle oa); rewrite -S1p.
  move=> [xs [xs' osr]]; ee.
  move: osr; rewrite /order_sum_r /glt {1} /gle canonical_doubleton_order_pr.
  case.
    move=> [aux nsQ]; case aux.
      by move=> [e1 e2]; rewrite e1 e2 in nsQ; elim nsQ.
    case; first by move=> [e1 e2]; rewrite e1 e2 in nsQ; elim nsQ.
    move=> [e1 e2]; right; right; rewrite e2; ee.
  move=> [sq].
  case (canonical_du2_pr2 xs) =>Qx; rewrite -sq Qx; bw; [left| right;left]; ee.
move=> [xs1 [x's1 etc]]; ee.
red; rewrite /glt/gle canonical_doubleton_order_pr.
case etc.
  move=> [qxa [qx'a lrxx']]; rewrite qxa qx'a; bw;right;ee.
case.
  move=> [qxa [qx'a lrxx']].
  have ->: (Q x' = TPb) by case (canonical_du2_pr2 x's1) => //.
  have ->: (Q x = TPb) by case (canonical_du2_pr2 xs1) => //.
  right; bw; ee.
move=> [qxa qx'a]; rewrite qxa.
have ->: (Q x' = TPb) by case (canonical_du2_pr2 x's1) => //.
intuition.
Qed.

Lemma order_sum2_gle_spec: forall r r' x x', order r -> order r' ->
  inc x (substrate r) -> inc x' (substrate r') ->
  glt (order_sum2 r r') (J x TPa) (J x' TPb).
Proof.
move=> r r' x x' or or' xsr xsr'.
rewrite /glt (order_sum2_gle _ _ or or').
split.
  split; first by apply canonical_du2_pra.
  split; first by apply canonical_du2_prb.
  right; right. aw. ee.
by move => h; move: (pr2_def h); apply two_points_distinct.
Qed.

Lemma order_prod2_gle: forall r r' a b,
  order r -> order r' ->
  gle (order_prod2 r r') a b =
  (inc a (product2 (substrate r') (substrate r))
    & inc b (product2 (substrate r') (substrate r))
    & ( (V TPa a = V TPa b & gle r (V TPb a) (V TPb b))
      \/ (glt r' (V TPa a)(V TPa b)))).
Proof.
move=> r r' a b or or'.
rewrite /order_prod2.
move: (order_prod2_axioms or or') => la.
rewrite order_prod_gle // substrate_canonical_doubleton_order.
have -> :prod_of_substrates (variantLc r' r) =
    (product2 (substrate r') (substrate r)).
  rewrite -order_prod2_substrate // /order_prod2; bw.
apply iff_eq; move => [ap2 [bp2 h]]; ee.
  case h.
     move => <-; left;ee; order_tac.
     by move: ap2; rewrite product2_rw; move => [_ [_ [_]]].
  move=> [j []]; rewrite two_points_rw; case => h'; rewrite h'; bw.
    intuition.
  move=> [p1 p2].
  have p3: (glt canonical_doubleton_order TPa TPb).
    split; [ rewrite /gle canonical_doubleton_order_pr; auto | fprops].
  move: (p2 _ p3) => p4; move: p1 => [p1 _]; auto.
case h.
  move=> [p1 p2]; case (equal_or_not (V TPb a) (V TPb b)).
    move:ap2 bp2; rewrite ! product2_rw.
    move => [fga [da [Vaa VBa]]][fgb [db [Vab VBb]]].
    move=> VV; left; apply fgraph_exten =>//; first by ue.
    rewrite da; move=> ix xda;try_lvariant xda.
  move => nVV; right; exists TPb; ee; bw; first by split.
  move=> i; rewrite /glt/gle canonical_doubleton_order_pr.
  move=> [p3 p4]; case p3.
     by move=> [r1 r2]; rewrite -r2 in r1; contradiction.
  case; first by move=> [r1 _]; contradiction.
  by move=> [ia _]; rewrite ia.
move=> [_ ne];right; exists TPa; bw;ee.
move=> i; rewrite /glt/gle canonical_doubleton_order_pr.
move=> [aux ineA]; case aux.
   move=> [p _]; contradiction.
case; by move=> [_ p]; elim two_points_distinct.
Qed.

Lemma order_sum_worder : forall r g,
  worder r -> substrate r = domain g -> fgraph g ->
  (forall i, inc i (domain g) -> worder (V i g)) ->
  worder (order_sum r g).
Proof.
move=> r g [or wor] sr fg alw.
have oa: order_sum_a r g by red;ee.
split; first by apply order_sum_order.
move=> x; bw => xs [x0 x0x].
set (A := fun_image x Q).
have Asr: (sub A (substrate r)).
  move=> t; rewrite /A; aw; move=> [z [zx]] <-.
  rewrite sr;move: (du_index_pr1 (xs _ zx)); intuition.
have neA: nonempty A by exists (Q x0); rewrite /A; aw; ex_tac.
move: (wor _ Asr neA)=> [a []]; aw; move=> aA ale.
set (B:= Zo x (fun z => Q z = a)).
set (C := fun_image B P).
move: (Asr _ aA); rewrite sr => adg.
have Cs: (sub C (substrate (V a g))).
  move=> t; rewrite /C; aw; move=> [z [zB]] <-.
  move: zB; rewrite /B Z_rw; move => [zx] <-.
  by move: (du_index_pr1 (xs _ zx)) => [_ [ok _]].
have neC: nonempty C.
  move: aA; rewrite /A; aw; move=> [z [zx qz]].
  exists (P z); rewrite /C; aw; exists z; ee; rewrite /B; apply Z_inc =>//.
move: (alw _ adg)=> [or' wor']; move: (wor' _ Cs neC) => [b []].
rewrite (substrate_induced_order or' Cs).
move=> bC leB.
have Jx: inc (J b a) x.
   move: bC; rewrite /C; aw; move=> [z [zB]] <-.
   move: zB; rewrite /B Z_rw; move=> [zx] <-; aw.
   by move: (du_index_pr1 (xs _ zx)) => [_ [_]] ->.
have ors: order (order_sum r g) by apply order_sum_order.
have xso: sub x (substrate (order_sum r g)) by bw.
exists (J b a); red; rewrite (substrate_induced_order ors xso).
split; first (by exact); move=> c cx.
move: (du_index_pr1 (xs _ cx))=> [Qc [Pc pc]].
rewrite (iorder_gle _ Jx cx) (order_sum_gle oa); ee.
red; rewrite pr1_pair pr2_pair.
have QcA: (inc (Q c) A) by rewrite /A;aw; ex_tac.
move: (ale _ QcA); aw => leac.
case (equal_or_not a (Q c)).
  move=> aQc; right;ee.
  have Pcb: (inc (P c) C).
     rewrite /C; aw; exists c; ee ; rewrite /B; apply Z_inc =>//.
  move: (leB _ Pcb); aw.
by move => nac; left; split.
Qed.

Lemma order_prod_worder: forall r g,
  worder r -> substrate r = domain g -> fgraph g ->
  (forall i, inc i (domain g) -> worder (V i g)) ->
  finite_set (substrate r) ->
  worder (order_prod r g).
Proof.
move=> r g wor sr fg alw fs.
have lea:order_prod_a r g by red; red;ee.
set n := cardinal (substrate r).
have: cardinal (substrate r) = n by [].
move: lea alw.
generalize r g.
have: inc n Bnat by move: fs; rewrite /finite_set inc_Bnat.
generalize n.
clear n fs wor sr fg r g.
apply: cardinal_c_induction.
  move=> r g lea alw cr.
  have lo: order (order_prod r g) by fprops.
  move: (cardinal_nonemptyset cr) => se; split; first by fprops.
  move=> x xsr; move: (xsr).
  bw; rewrite /prod_of_substrates /fam_of_substrates.
  move: lea => [wor [sr [fgg alo]]].
  rewrite -sr se product2_trivial.
  move=> sxs nex; exists emptyset; red; aw.
  move: nex=> [w wx]; move: (sxs _ wx); aw; move=> h; rewrite h in wx.
  ee; move=> t tx; rewrite iorder_gle =>//.
  have ->: t = emptyset by move: (sxs _ tx); aw.
  by order_tac; apply xsr.
move=> n nB hrec r g lea alwo csr.
have lo: (order (order_prod r g)) by fprops.
split =>//.
move: (lea) => [[or wor] [sr [fgg alo]]].
have [i [isr ilsr]]: exists i,
   inc i (domain g) & forall j, inc j (substrate r) -> gle r i j.
  case (emptyset_dichot (substrate r)).
    move=> se; rewrite se cardinal_emptyset in csr.
    symmetry in csr; elim (succ_nonzero csr).
  move=> nsr.
  move: (wor _ (@sub_refl (substrate r)) nsr) => [y []]; aw; last by fprops.
  rewrite -sr; move=> ysr aux; exists y; ee; move=> j jsr.
  apply (iorder_gle1 (aux _ jsr)).
bw.
move => x xsr [x0 x0x].
set (Y := fun_image x (fun z => V i z)).
have sYs: (sub Y (substrate (V i g))).
  move=> t; rewrite /Y; aw; move=> [z [zx]] <-.
  by move: (xsr _ zx); apply prod_of_substrates_pr.
have neY: (nonempty Y) by exists (V i x0); rewrite /Y; aw; ex_tac.
move: (alwo _ isr) => [or' wor']; move: (wor' _ sYs neY) => [z []]; aw.
clear wor wor'.
move => zY zl.
set (X1 := Zo x (fun t => V i t = z)).
have p1: (forall a b, inc a x -> inc b x -> inc a X1 -> ~ inc b X1 ->
    glt (order_prod r g) a b).
  move=> a b ax bx aX1 bX1; split.
    rewrite order_prod_gle =>//; ee.
    right; exists i; ee; first by ue.
      move: aX1; rewrite /X1 Z_rw; move=> [_ eq1]; rewrite eq1.
      have ib: z <> V i b.
        by move=> eq2; move: bX1; rewrite /X1 Z_rw; apply; ee.
      split =>//.
      have vbY: (inc (V i b) Y)by rewrite /Y; aw; exists b; auto.
      apply (iorder_gle1 (zl _ vbY)).
    move=> j rj.
    have jsr: (inc j (substrate r)) by order_tac.
    move: (ilsr _ jsr) => le2; order_tac.
  by dneg ab; rewrite -ab.
set (I1:= complement (substrate r) (singleton i)).
set (r1 := induced_order r I1).
set (g1 := restr g I1).
have I1sr: (sub I1 (substrate r)) by rewrite /I1; apply sub_complement.
have dg1: domain g1 = I1 by rewrite /g1 restr_domain1 //; ue.
have sdg1g: sub (domain g1) (domain g) by rewrite dg1; ue.
have la1: order_prod_a r1 g1.
  red; red;ee.
        move: lea => [WO _]; rewrite /r1; apply worder_restriction => //.
      symmetry; rewrite /r1; aw.
    rewrite /g1; fprops.
  move=> j j1; move: (alo _ (sdg1g _ j1)) => og1.
   rewrite /g1 restr_ev //; ue.
have alw: (forall i, inc i (domain g1) -> worder (V i g1)).
  move=> j jdg; rewrite /g1 restr_ev; [by apply alwo;apply sdg1g|exact| ue| ue].
have cs: (cardinal (substrate r1) = n).
  rewrite /r1; aw; move: (cardinal_succ_pr1 (substrate r) i).
  rewrite -sr in isr.
  rewrite -tack_on_complement // -/I1 csr => h.
  symmetry; apply succ_injective1; fprops.
move: (hrec _ _ la1 alw cs) =>[or1 wo1].
set (X2 := fun_image X1 (fun z => restr z I1)).
have X2s: sub X2 (substrate (order_prod r1 g1)).
  bw;rewrite /prod_of_substrates /fam_of_substrates.
  move=> t; rewrite /X2; aw; last (by gprops); move=> [u [uX1]] <-.
  have ux: (inc u x) by move: uX1; rewrite /X1 Z_rw; move=> [ok _].
  move: (xsr _ ux).
  rewrite /prod_of_substrates /fam_of_substrates; aw; bw; last by gprops.
  move=> [fgfu [du iVV]].
  have aa: sub I1 (domain u) by rewrite du -sr.
  have ->: domain (restr u I1) = I1 by rewrite restr_domain1 //.
  ee; move=> j jI1; bw; last by ue.
  move: (iVV _ (aa _ jI1)); bw; [ rewrite /g1 restr_ev //; ue | ue].
have neX2: (nonempty X2).
  move: zY; rewrite /Y; aw; move => [z0 [z0x pz0]]; exists (restr z0 I1).
  rewrite /X2; aw; exists z0; ee; rewrite /X1; apply Z_inc =>//.
move: (wo1 _ X2s neX2)=> [w []]; aw; move=> wX2 wle.
move: wX2; rewrite /X2; aw; move=> [v [vX1 rv]].
have vx: inc v x by move: vX1; rewrite /X1 Z_rw; intuition.
have sxs: sub x (substrate (order_prod r g)) by bw.
exists v; red; aw; split =>//.
move=> u ux1.
rewrite iorder_gle //.
case (equal_or_not (V i u) z)=> Vu; last first.
  have nv: (~ inc u X1) by rewrite /X1 Z_rw; dneg ww; intuition.
  by move: (p1 _ _ vx ux1 vX1 nv)=> [le _].
have uX1: (inc u X1) by rewrite /X1; apply Z_inc.
have rX2: (inc (restr u I1) X2) by rewrite /X2; aw; ex_tac.
move: (iorder_gle1 (wle _ rX2)).
rewrite /gle lexorder_gle // lexorder_gle //.
set T:= Zo _ _.
have aa:sub (Zo (domain g) (fun i0 : Set => V i0 v <> V i0 u)) (substrate r).
  by rewrite sr; apply Z_sub.
move=> [wp [rp op]]; ee; move=> j []; rewrite (substrate_induced_order or aa).
rewrite Z_rw; move=> [jd nsv] aux.
have jI1: inc j I1.
  rewrite /I1; srw; split; first (by ue); aw; dneg ij; rewrite ij Vu.
  move:vX1; rewrite /X1 Z_rw; intuition.
have aux1: product_order_axioms g by red; ee.
move: (xsr _ vx) (xsr _ ux1).
rewrite ! (prod_of_substrates_rw _ aux1); move=> [fgv [dv vp]][fgu [du up]].
have jT: inc j T.
   rewrite /T; apply Z_inc; first by ue.
   rewrite -rv ! restr_ev //; [ by rewrite du -sr | by rewrite dv -sr].
have lej: (least_element (induced_order r1 T) j).
  have or1': order r1 by rewrite /r1; fprops.
  have sT: sub T (substrate r1) by rewrite /T /r1; aw;rewrite -dg1; apply Z_sub.
  red; aw; split =>//.
  move=> x1 aux2; rewrite (iorder_gle _ jT aux2).
  move: aux2; rewrite Z_rw dg1; move=> [xdg1].
  rewrite -rv ! restr_ev =>//; try (rewrite ? du ? dv -sr; apply I1sr).
  move=> VV.
  have aux3: (inc x1 (Zo (domain g) (fun i : Set => V i v <> V i u))).
    by apply Z_inc =>//;rewrite -sr; apply I1sr.
  move: (iorder_gle1 (aux _ aux3)).
  rewrite /r1 (iorder_gle _ jI1 xdg1) //.
move: (op _ lej).
rewrite -rv ! restr_ev =>//; try (rewrite ? du ? dv -sr; apply I1sr).
Qed.

Lemma order_prod2_worder: forall r r',
  worder r -> worder r' -> worder (order_prod2 r r').
Proof.
move=> r r' wor wor'; rewrite/ order_prod2; apply order_prod_worder.
- apply worder_canonical_doubleton_order.
- rewrite substrate_canonical_doubleton_order; bw.
- fprops.
- bw; move=> i ind; try_lvariant ind.
- rewrite substrate_canonical_doubleton_order -two_points_pr2.
  apply doubleton_finite.
Qed.

Lemma order_sum2_worder: forall r r',
  worder r -> worder r' -> worder (order_sum2 r r').
Proof.
move=> r r' wor wor'; apply order_sum_worder.
- apply worder_canonical_doubleton_order.
- rewrite substrate_canonical_doubleton_order; bw.
- fprops.
- bw; move=> i ind; try_lvariant ind.
Qed.

Lemma order_sum2_totalorder: forall r r',
  total_order r -> total_order r' -> total_order (order_sum2 r r').
Proof.
move=> r r' [or tor][or' tor'].
split; first by fprops.
rewrite order_sum2_substrate// /gge.
move=> x y xc yc.
move: (xc)(yc); rewrite ! canonical_du2_pr; move=> [px p1][py p2].
rewrite ! (order_sum2_gle _ _ or or').
have ba: TPb <> TPa by fprops.
case p1; case p2; move=> [Py Qy][Px Qx]; rewrite Qx Qy.
      case (tor _ _ Px Py); [ left | rewrite /gge; right]; eee; left; ee.
    left; split; eee; right;right; ee.
  right; split; eee; right;right; ee.
case (tor' _ _ Px Py); [ left | rewrite /gge; right]; eee; right; left; eee.
Qed.

Lemma order_prod_pr: forall r r',
  order r -> order r' ->
 (order_prod2 r r') \Is (order_sum r' (cst_graph (substrate r') r)).
Proof.
move=>r r' or or'.
have oa: (order_sum_a r' (cst_graph (substrate r') r)).
  red; rewrite /cst_graph; ee; bw; move=> i isr; bw.
set (fo:=order_sum r' (cst_graph (substrate r') r)).
have odf: order fo by rewrite /fo; apply order_sum_order.
move: (order_sum_substrate oa) => s1.
move: (order_prod2_order or odf).
move: (order_prod2_substrate or or').
set (io := order_prod2 r r'); move => s2 o2.
set (f := fun x => J (V TPb x) (V TPa x)).
have ta: (transf_axioms f (substrate io) (substrate fo)).
  move=> t; rewrite /fo s1 s2 /sum_of_substrates /fam_of_substrates /cst_graph.
  rewrite product2_rw /f; move=> [fgt [dt [va vb]]].
  apply inc_disjoint_union; bw.
have bf: bijection (BL f (substrate io) (substrate fo)).
  apply bl_bijective =>//.
    move=> u v;rewrite s2 /f; move=> ud vd eq.
    move: (pr2_def eq)(pr1_def eq) => p1 p2.
    apply (productf_exten ud vd); move=> i itp; try_lvariant itp.
  rewrite s1 /fam_of_substrates /cst_graph.
  move=> y ys; move:(du_index_pr1 ys); rewrite L_domain.
  move=> [Qsr]; bw; move=> [Psr py].
  exists (variantLc (Q y) (P y)); split.
    rewrite s2 product2_rw; ee; bw.
  rewrite /f; bw; aw.
exists (BL f (substrate io) (substrate fo));red; ee; [ | aw | aw | aw].
  rewrite /io; apply order_prod2_order =>//.
move=> x y xs ys; rewrite /io order_prod2_gle // -s2.
have p1: inc (V TPa x) (substrate r').
    move: xs; rewrite s2 product2_rw; intuition.
rewrite bl_W // bl_W// /fo order_sum_gle // /order_sum_r.
apply iff_eq.
  have ->: sum_of_substrates (cst_graph (substrate r') r) = substrate fo by aw.
  move=> [xp [yp aux]]. ee;rewrite /f /cst_graph; bw; aw; case aux;auto.
move=> [xp [yp aux]]; eee;move: aux.
rewrite/f /cst_graph; aw; bw; case; auto.
Qed.

Lemma ordinal_p8: forall r r', worder r -> worder r' ->
  r \Is r' -> ordinal r = ordinal r'.
Proof.
move=> r r' w1 w2 or.
move: (ordinal_p1 w1)(ordinal_p1 w2) => o1 o2.
move: (ordinal_p2 w1)(ordinal_p2 w2) => i1 i2.
apply (ordinal_p4 o1 o2 (orderIT (orderIT (orderIS i1) or) i2)).
Qed.

Lemma ordinal_p9: forall r E, worder r -> is_ordinal E ->
  r \Is (ordinal_o E) -> ordinal r = E.
Proof.
move=> r E wo bo oi.
by rewrite (ordinal_p8 wo (ordinal_worder2 bo) oi) (ordinal_p3 bo).
Qed.

Lemma ordinal_p10: forall x, is_ordinal x -> worder (ordinal_o x).
Proof. exact ordinal_worder2. Qed.

Lemma ordinal_p11: forall x, is_ordinal x -> order (ordinal_o x).
Proof. by move=> x xB; apply worder_is_order; apply ordinal_p10. Qed.

Lemma worder_invariance: forall r r',
  r \Is r' -> worder r -> worder r'.
Proof.
move=> r r' [f [or [or' [bf [sf [tf sif]]]]]] [_ wor].
split =>//.
move=> x xsr [y yx].
set (A := image_by_fun (inverse_fun f) x).
move: (inverse_bij_is_bij bf) => bif.
have fi: is_function (inverse_fun f) by fct_tac.
have sxt: (sub x (target f)) by rewrite -tf.
have sxs: (sub x (source (inverse_fun f))) by aw.
have sx :source f = (target (inverse_fun f)) by aw.
have Asx: sub A (substrate r).
  rewrite /A sf; move=> t; aw; move=> [u [ux]] <-.
  by rewrite sx;apply inc_W_target =>//; apply sxs.
have neA: (nonempty A) .
  by exists (W y (inverse_fun f)); rewrite /A; aw; ex_tac.
move: (wor _ Asx neA) => [z []]; aw.
move=> zA zle; move: (zA).
rewrite /A; aw; move=> [u [ux Wu]].
have utx: (inc u (target f)) by apply sxt.
symmetry in Wu; move: (W_inverse bf utx Wu) => uW.
exists u; red; aw; split =>//.
move=> v vx; aw.
set v':= W v (inverse_fun f).
have vt: (inc v (target f)) by apply sxt.
move: (W_inverse bf vt (refl_equal v')).
rewrite uW; move=> ->.
have vA: inc v' A by rewrite /A; aw; ex_tac.
 move: (iorder_gle1 (zle _ vA)).
by rewrite sif //; rewrite -sf; apply Asx.
Qed.

Lemma ordinal_pr51: forall x, is_ordinal x -> order (ordinal_o x).
Proof. by move=> t or; move: (ordinal_p10 or) => [res _]. Qed.

Lemma ordinal_pr52: forall x, is_ordinal x -> total_order (ordinal_o x).
Proof. by move=> t or; apply (worder_total (ordinal_p10 or)). Qed.

Lemma order_le_reflexive: forall x, order x -> order_le x x.
Proof.
move=> x ox; red; ee; exists (identity (substrate x)); exists (substrate x).
split; first by fprops.
red; aw;ee; first by apply identity_bijective.
move=> z y xsr ysr; rewrite ! identity_W //; aw.
Qed.

Lemma set_ord_le_prop: forall a x, is_ordinal a ->
  inc x (succ_o a) = x <=o a.
Proof.
move=> a x oa;rewrite ordinal_le_pr0.
apply iff_eq.
  move=> xo; ee.
    apply (elt_of_ordinal (ordinal_succ oa) xo).
  apply (ordinal_sub3 oa xo).
by move=> [ox [_ s]]; rewrite - (ordinal_sub4 ox oa).
Qed.

Lemma set_ord_lt_prop: forall a x, is_ordinal a ->
  inc x a = x <o a.
Proof.
move=> a x oa;rewrite ordinal_lt_pr0.
apply iff_eq; last by intuition.
move=> xa;ee; ord_tac.
Qed.

Lemma set_ord_lt_prop3: forall a, is_ordinal a ->
  ordinal (graph_on ordinal_le a) = a.
Proof.
move=> a oa.
have [p1 p2]: (substrate (graph_on ordinal_le a) = a &
  worder (graph_on ordinal_le a)).
  apply wordering_pr; first by apply wordering_ordinal_le.
  move=> b bx; apply (ordinal_le_reflexive (elt_of_ordinal oa bx)).
have o1: order (graph_on ordinal_le a) by fprops.
have -> : graph_on ordinal_le a = ordinal_o a.
  rewrite graph_exten ; try apply order_is_graph; fprops.
  move=> u v.
  rewrite graph_on_rw1 ordinal_le_pr0; aw.
  apply iff_eq; first by intuition.
  by move=> [ua [va uv]]; ee; apply (elt_of_ordinal oa).
apply (ordinal_p3 oa).
Qed.

Lemma finite_ordinal1: forall n, inc n Bnat ->
   n = ordinal (interval_Bnatco n).
Proof.
move=> n nB.
have cn: is_cardinal n by fprops.
have ox: is_ordinal n by apply cardinal_ordinal.
have cnn: cardinal n = n by apply cardinal_of_cardinal.
have fs: finite_set n by red; rewrite cnn -inc_Bnat.
move: (interval_Bnatco_worder nB) => w1.
move: (ordinal_p1 w1) => on.
apply (ordinal_p4 ox on).
move: (worder_total (ordinal_p10 ox)) => p1.
move: (worder_total (ordinal_p10 on)) => p2.
have p3: equipotent (substrate (ordinal_o n))
    (substrate (ordinal_o (ordinal (interval_Bnatco n)))).
  eqtrans (substrate (interval_Bnatco n)).
    rewrite - cardinal_equipotent interval_Bnatco_substrate //.
    rewrite cardinal_interval_co_0a1 // (ordinal_p7 ox) //.
  move: (ordinal_p2 w1) => [f [_ [_ [bf [sf [tf p]]]]]].
  rewrite sf tf; exists f; ee.
have fs1: finite_set (substrate (ordinal_o n)) by rewrite (ordinal_p7 ox).
move: (isomorphism_worder_finite p1 p2 fs1 p3) => [[f fiso] _].
exists f; ee.
Qed.

Lemma finite_ordinal2: forall n, inc n Bnat ->
   n = interval_co_0a n.
Proof.
move=> n nB.
have cn: is_cardinal n by fprops.
move: (cardinal_ordinal cn) => on.
set_extens t; rewrite (interval_co_0a_pr2 _ nB) /cardinal_lt /cardinal_le.
  move => tn; split.
    have fn: finite_o n by move: nB; rewrite inc_Bnat.
    move: (finite_o_increasing tn fn) => [ot ft].
    move: (ordinal_transitive on tn) => stn.
    ee; apply finite_cardinal_pr1; red; ee.
  by move => nt; rewrite nt in tn; elim (ordinal_decent on tn).
move => [[ct [_ tn]] nt].
apply (on _ tn (ordinal_transitive (cardinal_ordinal ct)) nt).
Qed.

Definition ord_zero := card_zero.
Definition ord_one := card_one.
Definition ord_two := card_two.
Definition ord_omega := omega0.

Notation "0 'o'" := ord_zero.
Notation "1 'o'" := ord_one.
Notation "2 'o'" := ord_two.
Notation "\omega" := ord_omega.

Lemma ordinal_0: is_ordinal 0o.
Proof. apply ordinal_zero. Qed.

Lemma ordinal_1: is_ordinal 1o.
Proof. apply ordinal_one. Qed.

Lemma ordinal_2: is_ordinal 2o.
Proof. apply ordinal_two. Qed.

Lemma ordinal_omega: is_ordinal \omega.
Proof. apply omega0_ordinal. Qed.

Lemma ord_omega_pr: \omega = ordinal Bnat_order.
Proof.
move: Bnat_order_worder => bwo.
have <-: (ordinal_o \omega = Bnat_order).
  rewrite graph_exten; try apply order_is_graph; fprops.
  move=> u v;rewrite -[related]/gle.
  rewrite Bnat_order_le /Bnat_le /cardinal_le; aw; apply iff_eq.
    move=> [p1 [p2 p3]]; ee.
  move=> [p1 [p2 [p3 [p4 p5]]]]; ee.
symmetry;apply ordinal_p3; apply ordinal_omega.
Qed.

Lemma ordinal_finite1: forall x, is_ordinal x ->
  finite_set x -> finite_o x.
Proof.
move=> c ox fsx;red; ee.
move=> infc; rewrite (omega0_pr1 ox) in infc.
move: (sub_finite_set infc fsx).
move: infinite_Bnat.
rewrite /Bnat /finite_set => h1 h2; elim (infinite_dichot2 h2 h1).
Qed.

Lemma ordinal_finite2: forall x, is_ordinal x ->
  infinite_set x -> infinite_o x.
Proof.
move=> x bx ifs; ex_middle fo.
have fx: finite_o x by split.
move: (finite_cardinal_pr1 fx) => cx.
by red in ifs;move: ifs; rewrite (cardinal_of_cardinal cx).
Qed.

Lemma ordinal_finite3: forall x, is_ordinal x ->
  finite_set x -> x <o \omega.
Proof.
move=> x Bx ifs.
rewrite ordinal_lt_pr0; ee;first by apply omega0_ordinal.
by rewrite omega0_pr2 => //; apply ordinal_finite1.
Qed.

Lemma ordinal_finite4: forall x, is_ordinal x ->
  infinite_set x -> \omega <=o x.
Proof.
move=> x Bx ifs.
rewrite ordinal_le_pr0; ee;first by apply omega0_ordinal.
by rewrite -omega0_pr1 => //; apply ordinal_finite2.
Qed.

Lemma cardinal_Bnat: cardinal Bnat = Bnat.
Proof. apply cardinal_of_cardinal; apply omega0_cardinal. Qed.

Properties of zero

Lemma emptyset_order: order emptyset.
Proof.
have <-: (inclusion_suborder emptyset=emptyset).
  apply is_emptyset;move=> y; rewrite /inclusion_suborder /graph_on Z_rw.
  aw; move=> [[_ [pe _]] _]; elim (emptyset_pr pe).
fprops.
Qed.

Lemma emptyset_substrate : substrate emptyset = emptyset.
Proof.
apply is_emptyset; move: emptyset_order => eo y ys.
have pe: (inc (J y y) emptyset) by order_tac.
elim (emptyset_pr pe).
Qed.

Lemma emptyset_worder: worder emptyset.
Proof.
split; first by apply emptyset_order.
rewrite emptyset_substrate.
move=> x xs [y yx]; elim (emptyset_pr (xs _ yx)).
Qed.

Lemma empty_substrate_zero: forall x, order x -> substrate x = emptyset ->
   x = emptyset.
Proof.
move=> x ox sx; apply is_emptyset;move=> y yx.
have gx : is_graph x by fprops.
have yp: y = J (P y) (Q y) by aw; rewrite gx.
rewrite yp in yx.
empty_tac1 (P y); substr_tac.
Qed.

Lemma ordinal_o_emptyset: ordinal_o emptyset = emptyset.
Proof. rewrite /ordinal_o; apply empty_substrate_zero; [fprops | aw ]. Qed.

Lemma ordinal0_emptyset: 0o = emptyset.
Proof. trivial. Qed.

Lemma ordinal0_pr: ordinal emptyset = 0o.
Proof.
have <-: ordinal_o emptyset = emptyset.
  rewrite /ordinal_o; apply empty_substrate_zero; [fprops| aw].
by rewrite (ordinal_p3 ordinal_0).
Qed.

Lemma ordinal0_pr1: forall x, order x -> substrate x = emptyset ->
  ordinal x = 0o.
Proof.
move=> x ox sx.
rewrite (empty_substrate_zero ox sx) ordinal0_pr //.
Qed.

Lemma singleton_worder: forall x,
  let E := singleton (J x x) in
   substrate E = singleton x & worder E.
Proof.
move=> x E.
have Ei: (E = identity_g (singleton x)).
  rewrite /identity_g /E /L.
  set_extens t; aw; [ move=> ->;ex_tac | move=> [z [zs]] <-; awi zs; ue].
have oE: (order E) by rewrite Ei; apply diagonal_order.
have sE: (substrate E = singleton x).
  set_extens y;aw.
    move => ys.
    have: (related E y y) by order_tac.
    rewrite /related /E; aw; apply pr1_def.
  move=> ->; have JE: (inc (J x x) E) by rewrite /E; fprops.
  substr_tac.
split =>//; split =>//.
move=> y yE [z zy]; exists z; red; aw;split => //.
move=> t ty; aw.
rewrite sE in yE; move: (yE _ zy)(yE _ ty); aw; move=> -> ->.
red; red; rewrite /E; fprops.
Qed.

Lemma singleton_order_isomorphic: forall x y,
  (singleton (J x x)) \Is (singleton (J y y)).
Proof.
move=> x y;set (f := fun _: Set => y).
move: (singleton_worder x) => [s1 [o1 _]].
move: (singleton_worder y) => [s2 [o2 _]].
have ta: transf_axioms f (singleton x) (singleton y) by move=> t;aw.
exists (BL f (singleton x) (singleton y)).
red; ee; aw.
  apply bl_bijective => //.
    by move=> u v; aw; move => -> ->.
  move=> t; aw; move=> ->; exists x; aw; rewrite /f; ee.
move=> a b; rewrite ! singleton_rw; move=> -> ->; aw.
rewrite /f/ gle/related;aw; apply iff_eq; intuition.
Qed.

Lemma singleton_order_isomorphic1: forall x,
  order x -> is_singleton (substrate x) ->
  exists v, x = singleton (J v v).
Proof.
move=> x or [u sxsu]; exists u; set_extens v.
  move=> vx; aw.
  have gx: (is_graph x) by fprops.
  have p: (v = J (P v) (Q v)) by rewrite (gx _ vx).
  rewrite p in vx.
  have : (inc (P v) (substrate x)) by substr_tac.
  have : (inc (Q v) (substrate x)) by substr_tac.
  rewrite sxsu; aw => p1 p2.
  by rewrite -{2} p1 -p2.
aw; move=> ->; order_tac; rewrite sxsu; fprops.
Qed.

Lemma singleton_order_isomorphic2: forall x y,
  order x -> order y ->
  is_singleton (substrate x) -> is_singleton (substrate y)
  -> x \Is y.
Proof.
move=> x y ox oy ssx ssy;
move: (singleton_order_isomorphic1 ox ssx)=> [u] ->.
move: (singleton_order_isomorphic1 oy ssy)=> [v] ->.
apply singleton_order_isomorphic.
Qed.

Lemma ordinal1_pr: forall x, ordinal (singleton (J x x)) = 1o.
Proof.
move=> x; move: (singleton_worder x) => [p1 p2].
apply (ordinal_p9 p2 ordinal_1).
apply singleton_order_isomorphic2; fprops.
  by rewrite p1; exists x.
by rewrite /ordinal_o /1o /card_one; aw; exists emptyset.
Qed.

Lemma singleton_ordinal: forall x,
  order x -> is_singleton (substrate x) ->
  ordinal x = 1o.
Proof.
move=> x ox sx; move: (singleton_order_isomorphic1 ox sx) => [y xy].
by rewrite xy ordinal1_pr.
Qed.

Definition ord_sum r g :=
   ordinal (order_sum r (L (domain g) (fun z => (ordinal_o (V z g))))).
Definition ord_prod r g :=
   ordinal (order_prod r (L (domain g) (fun z => (ordinal_o (V z g))))).
Definition ord_sum2 a b := ordinal (order_sum2 (ordinal_o a) (ordinal_o b)).
Definition ord_prod2 a b := ordinal (order_prod2 (ordinal_o a) (ordinal_o b)).

Notation "x +o y" := (ord_sum2 x y) (at level 50).
Notation "x *o y" := (ord_prod2 x y) (at level 50).

Lemma variantLc_comp: forall a b f,
 (variantLc (f a) (f b)) = (L (domain (variantLc a b))
    (fun z => f (V z (variantLc a b)))).
Proof.
move=> a b f; apply fgraph_exten; gprops; fprops; bw.
move=> x xtp; try_lvariant xtp; fprops.
Qed.

Lemma ord_sum2_pr: forall a b,
  a +o b = ord_sum canonical_doubleton_order (variantLc a b).
Proof.
move=> a b; rewrite /ord_sum2 /ord_sum /order_sum2.
by rewrite variantLc_comp.
Qed.

Lemma ord_prod2_pr: forall a b,
  a *o b = ord_prod canonical_doubleton_order (variantLc b a).
Proof.
move=> a b; rewrite /ord_prod2 /ord_prod /order_prod2.
by rewrite variantLc_comp.
Qed.

Lemma ord_sum_ordinal: forall r g,
  worder r -> substrate r = domain g -> fgraph g ->
  (forall i, inc i (domain g) -> is_ordinal (V i g))
  -> is_ordinal (ord_sum r g).
Proof.
move=> r g or sr dg alo; rewrite /ord_sum; apply ordinal_p1.
apply order_sum_worder=>//; bw.
  gprops.
move=> i idg; bw; apply ordinal_p10; apply (alo _ idg).
Qed.

Lemma ord_prod_ordinal: forall r g,
  worder r -> substrate r = domain g -> fgraph g ->
  (forall i, inc i (domain g) -> is_ordinal (V i g))
  -> finite_set (substrate r)
  -> is_ordinal (ord_prod r g).
Proof.
move=> r g or sr dg alo fs; rewrite /ord_prod; apply ordinal_p1.
apply order_prod_worder =>//; bw.
  gprops.
move=> i idg; bw; apply ordinal_p10; apply (alo _ idg).
Qed.

Lemma ord_sum2_ordinal: forall a b, is_ordinal a -> is_ordinal b ->
  is_ordinal (a +o b).
Proof.
move=> a b wo1 wo2;rewrite /ord_sum2; apply ordinal_p1.
by apply order_sum2_worder; apply ordinal_p10.
Qed.

Lemma ord_prod2_ordinal: forall a b, is_ordinal a -> is_ordinal b ->
  is_ordinal (a *o b).
Proof.
move=> a b wo1 wo2;rewrite /ord_sum2; apply ordinal_p1.
by apply order_prod2_worder; apply ordinal_p10.
Qed.

Lemma ord_sum_invariant1: forall r r' f g g',
  order r -> substrate r = domain g -> fgraph g ->
  order r' -> substrate r' = domain g' -> fgraph g' ->
  order_isomorphism f r r' ->
  (forall i, inc i (substrate r) -> (V i g) \Is (V (W i f) g'))
  -> (order_sum r g) \Is (order_sum r' g').
Proof.
move=> r r' f g g' or sr fgg or' sr' fgf' oi ali.
have oa: (order_sum_a r g).
  by red;ee; rewrite -sr=> i idg; move: (ali _ idg)=> [h [o1 [o2 _]]].
move: oi => [_ [_ [[[ff ijf] sjf] [srf [tgf oif]]]]].
have oa': order_sum_a r' g'.
  red;ee; rewrite -sr' tgf=> i idg.
  move: (surjective_pr2 sjf idg) => [x [xsf]] <-.
  by rewrite -srf in xsf; move: (ali _ xsf)=> [h [o1 [o2 _]]].
move: (order_sum_order oa) (order_sum_order oa') => h1 h2; aw.
set (fa := fam_of_substrates g).
set (fb := fam_of_substrates g').
set (oj := fun i =>
    choose (fun z => order_isomorphism z (V i g) (V (W i f) g'))).
have ojp: (forall i, inc i (substrate r) ->
    order_isomorphism (oj i) (V i g) (V (W i f) g')).
  by move=> i isr; rewrite /oj; apply choose_pr; move: (ali _ isr).
set (h := fun x=> J (W (P x) (oj (Q x))) (W (Q x) f)).
have ta: (forall x, inc x (disjoint_union fa) -> inc (h x) (disjoint_union fb)).
  move=> x xd; move: (du_index_pr xd); rewrite /fa/fam_of_substrates.
  rewrite L_domain; move=> [qd]; bw; move => [ps px].
  rewrite /h.
  have wq: (inc (W (Q x) f) (domain g')).
    rewrite -sr' tgf. by apply inc_W_target; [ fct_tac | rewrite -srf sr].
  rewrite /fb /fam_of_substrates.
  apply inc_disjoint_union; bw.
  rewrite sr in ojp;move: (ojp _ qd) => [_ [_ [fx [sx [tx _]]]]].
  by rewrite tx; apply inc_W_target; [ fct_tac | ue].
have ta1: (transf_axioms h (substrate (order_sum r g))
    (substrate(order_sum r' g'))) by bw.
exists (BL h (substrate (order_sum r g)) (substrate (order_sum r' g'))).
red; rewrite bl_source bl_target; ee; bw.
  apply bl_bijective =>//.
    rewrite /h;move=> u v us vs eq; move:(du_index_pr1 us) (du_index_pr1 vs).
    move=> [Qu [Pu pu]][Qv [Pv pv]].
    move: (pr2_def eq) (pr1_def eq) => r1 r2.
    have sQ: (Q u = Q v) .
      apply ijf; [rewrite -srf;ue | rewrite -srf;ue | exact ].
    rewrite -sQ in r2 Pv; rewrite -sr in Qu.
    move: (ojp _ Qu) => [_ [_ [[[_ ijfj] _] [sfj _]]]].
    apply pair_exten =>//; apply ijfj =>//; ue.
  move=> y ys; move: (du_index_pr1 ys)=> [Qy [Py py]].
  rewrite -sr' tgf in Qy; move :(surjective_pr2 sjf Qy) =>[x [xsf Wx]].
  rewrite -srf in xsf; move: (ojp _ xsf) => [_ [_ [[_ sjj] [sfj [tjj _]]]]].
  rewrite -Wx tjj in Py; move: (surjective_pr2 sjj Py)=> [x0 [x0s x0w]].
  exists (J x0 x); ee.
    apply inc_disjoint_union1; ue.
  rewrite /h; aw; rewrite Wx x0w; aw.
move=> x y xs ys; aw; move: ta1; bw => ta2.
rewrite (order_sum_gle oa) (order_sum_gle oa').
move: (ta2 _ xs)(ta2 _ ys).
set (q1:= inc (h x) (sum_of_substrates g')).
set (q2:= inc (h y) (sum_of_substrates g')).
move => q1t q2t.
rewrite /h.
move: (du_index_pr1 xs) (du_index_pr1 ys) => [Qx [Px px]][Qy [Py py]].
have aux: (glt r' (W (Q x) f) (W (Q y) f) = glt r (Q x) (Q y)).
  rewrite -sr srf in Qx Qy.
  rewrite /glt; rewrite oif //; apply iff_eq; move=> [p1 p2]; ee.
    by dneg xy; rewrite xy.
rewrite /order_sum_r ! pr1_pair ! pr2_pair aux.
rewrite -srf sr in ijf; move: (ijf _ _ Qx Qy) => p0.
rewrite -sr in Qx; move: (ojp _ Qx) => [_ [_ [_ [sfj [tjj ojj]]]]].
rewrite -sfj in ojj.
apply iff_eq; move=> [xs1 [ys1 lep]]; ee; case lep.
      move=> qlt; auto.
    move=> [p1 p2]; rewrite -p1 in Py |- *.
    right; ee; rewrite -ojj //.
   move=> qlt; auto.
   move=> [p1 p2].
move:(p0 p1) => sq; right; split => //.
rewrite -sq in Py p2 ojj.
rewrite ojj //.
Qed.

Lemma ord_sum_invariant2: forall r g g',
  order r -> substrate r = domain g -> fgraph g ->
  substrate r = domain g' -> fgraph g' ->
  (forall i, inc i (substrate r) -> (V i g) \Is (V i g'))
  -> (order_sum r g) \Is (order_sum r g').
Proof.
move=> r g g' or sr fgg sr' fgg' ai.
apply (ord_sum_invariant1 or sr fgg or sr' fgg' (identity_isomorphism or)).
move => i isr; rewrite identity_W //; apply ai =>//.
Qed.

Lemma ord_sum_invariant3: forall r g,
  worder r -> substrate r = domain g -> fgraph g ->
  (forall i, inc i (domain g) -> worder (V i g)) ->
  ordinal (order_sum r g) =
    ord_sum r (L (substrate r) (fun i => ordinal (V i g))).
Proof.
move => r g wor sr fgg alo.
have or: order r by fprops.
rewrite /ord_sum; apply ordinal_p8.
  apply order_sum_worder => //.
  apply order_sum_worder; [ exact | bw | gprops | ].
    bw; move=> i isr; bw.
    rewrite sr in isr; apply (ordinal_p10 (ordinal_p1 (alo _ isr))).
apply ord_sum_invariant2; ee; [ bw | bw].
rewrite sr;move=> i isr ; bw; apply (ordinal_p2 (alo _ isr)).
Qed.

Lemma ord_sum_invariant4: forall r1 r2 r3 r4,
  r1 \Is r3 -> r2 \Is r4 ->
  (order_sum2 r1 r2) \Is (order_sum2 r3 r4).
Proof.
move=> r1 r2 r3 r4 h1 h2; rewrite /order_sum2.
apply ord_sum_invariant2; rewrite ?substrate_canonical_doubleton_order; bw.
      by move: worder_canonical_doubleton_order => [res _].
    fprops.
   fprops.
move=> i itp; try_lvariant itp.
Qed.

Lemma ord_prod_invariant1: forall r r' f g g',
  worder r -> substrate r = domain g -> fgraph g ->
  order r' -> substrate r' = domain g' -> fgraph g' ->
  order_isomorphism f r r' ->
  (forall i, inc i (substrate r) -> (V i g) \Is (V (W i f) g'))
  -> (order_prod r g) \Is (order_prod r' g').
Proof.
move=> r r' f g g' wor sr fgg or' sr' fgf' oif ali.
have aux: (order_isomorphic r r') by by exists f.
move: (worder_invariance aux wor) => wor'.
clear aux.
have bf: bijection f by move: oif => [_ [_ [bf _]]].
move: (oif) => [or [_ [[[ff ijf] sjf] [srf [tgf oif']]]]].
have oa: (lexicographic_order_axioms r g).
  by red;ee; rewrite -sr=> i idg; move: (ali _ idg)=> [h [o1 [o2 _]]].
have oa': lexicographic_order_axioms r' g'.
  red;ee. rewrite -sr' tgf=> i idg.
  move: (surjective_pr2 sjf idg) => [x [xsf]] <-.
  by rewrite -srf in xsf; move: (ali _ xsf)=> [h [o1 [o2 _]]].
move: (lexorder_order oa)(lexorder_order oa') => o1 o2; aw.
set (fa := fam_of_substrates g).
set (fb := fam_of_substrates g').
set (oi := fun i =>
    choose (fun z => order_isomorphism z(V i g) (V (W i f) g'))).
have oip: (forall i, inc i (substrate r) ->
    order_isomorphism (oi i) (V i g) (V (W i f) g')).
  by move=> i isr; apply choose_pr; by move:(ali _ isr).
set (fi := fun i => W i (inverse_fun f)).
have fip: (forall i, inc i (substrate r') -> W (fi i) f = i).
   move=> i isr; symmetry; apply W_inverse =>//; ue.
have fis: (forall i, inc i (substrate r') -> inc (fi i) (substrate r)).
  rewrite tgf srf; move=> i isf; apply W_inverse3 =>//.
set (fj := fun i => W i f).
have fjs: forall i, inc i (substrate r) -> (inc (fj i) (substrate r')).
   rewrite srf tgf /fj; move=> i isg; fprops.
have fij: forall i, inc i (substrate r) -> fi (fj i) = i.
  rewrite srf /fi/fj;move => i idf; symmetry;apply W_inverse2 =>//.
set (h := fun z=> L (substrate r') (fun i => W (V (fi i) z) (oi (fi i)))).
set (A := substrate(lexicographic_order r g)).
set (B := substrate(lexicographic_order r' g')).
have HA : A = (prod_of_substrates g) by rewrite /A lexorder_substrate.
have HB : B = (prod_of_substrates g') by rewrite /B lexorder_substrate.
have ta: (forall z, inc z A -> inc (h z) B).
  rewrite HA HB /prod_of_substrates /fam_of_substrates /h.
  move => z; rewrite productb_rw; try apply L_fgraph; move=> [fgf [dz iVV]].
  rewrite productb_rw; try apply L_fgraph; ee; first by bw.
  bw; move=> i isr; bw; try ue.
  move: (fis _ isr) => fisr.
  move: (oip _ fisr) => [_ [_ [boi [soi [toi _ ]]]]].
  move: dz; bw; move=> dz; rewrite dz -sr in iVV.
  rewrite (fip _ isr) in toi; rewrite toi;apply inc_W_target; first by fct_tac. move: (iVV _ fisr); bw; ue.
have ta': (forall w, inc w B -> exists z, inc z A & h z = w).
  rewrite HA HB /prod_of_substrates /fam_of_substrates /h => w.
  rewrite productb_rw; try apply L_fgraph; move=> [fgf [dz iVV]].
  exists (L (substrate r) (fun i => W (V (fj i) w) (inverse_fun (oi i)))).
  move: dz; bw => dz.
  split.
    aw; last (by gprops); ee; bw.
    move=> i isr.
    have idg: inc i (domain g) by ue.
    bw;move: (oip _ isr) => [_ [_ [boi [soi [toi _ ]]]]].
    rewrite soi; apply W_inverse3 =>//; rewrite -toi.
    move: (fjs _ isr) => fjsr.
    rewrite dz -sr' in iVV; move: (iVV _ fjsr); bw.
  symmetry; apply fgraph_exten; [exact | gprops | bw; ue | ].
  move=> x xdw.
  have xsr: inc x (substrate r') by rewrite sr' -dz.
  have fisr: inc (fi x) (substrate r) by apply fis.
  bw; rewrite /fj fip //.
  move: (oip _ fisr) => [_ [_ [boi [soi [toi _ ]]]]].
  move: (iVV _ xdw); bw; try ue; move => p1.
  by apply W_inverse =>//; rewrite -toi fip.
have hi: (forall z z', inc z A -> inc z' A -> h z = h z' -> z = z').
  have fgf: (fgraph (fam_of_substrates g)).
    rewrite /fam_of_substrates; gprops.
  rewrite HA => z z' zA zA';move: (zA) (zA'); rewrite/ prod_of_substrates.
  move=> p1 p2 p3; apply (productb_exten fgf p1 p2).
  rewrite /fam_of_substrates; bw.
  move=> i idg; move: (prod_of_substrates_pr idg p1) =>p4.
  move: (prod_of_substrates_pr idg p2)=> p5.
  rewrite -sr in idg; move: (fij _ idg)(fjs _ idg) => p6 p7.
  move: (f_equal (V (fj i)) p3); rewrite /h; bw; rewrite p6.
  move: (oip _ idg) => [_ [_ [[[_ ioi] _] [soi [toi _ ]]]]].
  apply ioi; ue.
have ta2: (transf_axioms h A B) by [].
exists (BL h A B).
red; ee;aw; first by apply bl_bijective.
move=> x y xA yA; rewrite bl_W // bl_W //.
rewrite /h.
rewrite ! order_prod_gle //.
rewrite -HA -HB.
move: (ta _ xA) (ta _ yA); rewrite /h.
set xv:= L _ _; set yv:= L _ _.
move=> xvB yvB.
apply iff_eq; move=> [_ [_ hyp]]; split; try exact; split; try exact.
   rewrite /xv /yv;case hyp=> aux; first by left; rewrite aux.
   move: aux => [j [jst [jlt ieq]]]; right.
   move: (fjs _ jst)(fij _ jst) => fjst fijt; ex_tac.
   split.
     bw; rewrite fijt; apply (order_isomorphism_pr2 (oip _ jst)) => //.
   move=> i ifj.
   have isr: inc i (substrate r') by order_tac.
   bw;rewrite (ieq (fi i)) // (order_isomorphism_pr0 oif) //.
      rewrite fip //.
   apply fis =>//.
case hyp=> aux; first by left; apply hi =>//.
right; move: aux => [j [jsr [jlt jle]]].
move: (fip _ jsr)(fis _ jsr) => fipa fisa.
ex_tac; split.
  move: jlt ; rewrite /xv /yv; bw.
  set a:= (V (fi j) x); set b:= (V (fi j) y).
  have ais: inc a (substrate (V (fi j) g)) by apply prod_of_substrates_pr; ue.
  have bis: inc b (substrate (V (fi j) g)) by apply prod_of_substrates_pr; ue.
  rewrite (order_isomorphism_pr0 (oip _ fisa) ais bis) fipa //.
move=> i lti.
move: (order_isomorphism_pr2 oif lti) => aux.
have fis': (inc (W i f) (substrate r')) by order_tac.
move: aux; rewrite fipa=> aux.
move: (jle _ aux); rewrite /xv /yv; bw.
have isr: inc i (substrate r) by order_tac.
rewrite fij //.
move: (oip _ isr) => [_ [_ [[[_ ioi] _] [soi [toi _ ]]]]].
apply ioi;rewrite -soi.
  move: xA; rewrite HA; apply prod_of_substrates_pr; ue.
  move: yA; rewrite HA; apply prod_of_substrates_pr; ue.
Qed.

Lemma ord_prod_invariant2: forall r g g',
  worder r -> substrate r = domain g -> fgraph g ->
  substrate r = domain g' -> fgraph g' ->
  (forall i, inc i (substrate r) -> (V i g) \Is (V i g'))
  -> (order_prod r g) \Is (order_prod r g').
Proof.
move=> r g g' wor sr fgf sr' fgg' alo.
have or: (order r) by move: wor => [or _].
apply (ord_prod_invariant1 wor sr fgf or sr' fgg' (identity_isomorphism or)).
by move=> i isr; rewrite identity_W //; apply alo.
Qed.

Lemma ord_prod_invariant3: forall r g,
  worder r -> substrate r = domain g -> fgraph g ->
  (forall i, inc i (domain g) -> worder (V i g)) ->
  finite_set (substrate r) ->
  ordinal (order_prod r g) =
    ord_prod r (L (substrate r) (fun i => ordinal (V i g))).
Proof.
move => r g wor sr fgg alo fsr.
have or: order r by fprops.
rewrite /ord_prod; apply ordinal_p8.
    apply order_prod_worder => //.
  apply order_prod_worder; [ exact | bw | gprops | | exact ].
  bw; move=> i isr; bw.
  rewrite sr in isr; apply (ordinal_p10 (ordinal_p1 (alo _ isr))).
apply ord_prod_invariant2; ee; [ bw | bw].
rewrite sr;move=> i isr ; bw; apply (ordinal_p2 (alo _ isr)).
Qed.

Lemma ord_prod_invariant4: forall r1 r2 r3 r4,
  r1 \Is r3 -> r2 \Is r4 ->
  (order_prod2 r1 r2) \Is (order_prod2 r3 r4).
Proof.
move=> r1 r2 r3 r4 h1 h2; rewrite /ord_prod2 /order_prod2.
apply ord_prod_invariant2; rewrite ?substrate_canonical_doubleton_order; bw.
      apply worder_canonical_doubleton_order.
    fprops.
   fprops.
move=> i itp; try_lvariant itp.
Qed.

Lemma ord_sum_invariant5: forall a b c, worder a -> worder b ->
  (order_sum2 a b) \Is c ->
  (ordinal a) +o (ordinal b) = ordinal c.
Proof.
move=> a b c oa ob h.
rewrite /ord_sum2; apply ordinal_p8.
  apply order_sum2_worder.
    apply (ordinal_p10 (ordinal_p1 oa)).
    apply (ordinal_p10 (ordinal_p1 ob)).
  move: h (order_sum2_worder oa ob); apply worder_invariance.
apply orderIT with (order_sum2 a b) => //; apply ord_sum_invariant4.
  apply orderIS; apply (ordinal_p2 oa).
apply orderIS; apply (ordinal_p2 ob).
Qed.

Lemma ord_prod_invariant5: forall a b c, worder a -> worder b->
  (order_prod2 a b) \Is c ->
  (ordinal a) *o (ordinal b) = ordinal c.
Proof.
move=> a b c oa ob h.
rewrite /ord_prod2; apply ordinal_p8.
  apply order_prod2_worder.
    apply (ordinal_p10 (ordinal_p1 oa)).
    apply (ordinal_p10 (ordinal_p1 ob)).
  move: h (order_prod2_worder oa ob); apply worder_invariance.
apply orderIT with (order_prod2 a b) => //; apply ord_prod_invariant4.
  apply orderIS; apply (ordinal_p2 oa).
apply orderIS; apply (ordinal_p2 ob).
Qed.

Lemma order_prod_pr1: forall a b c,
  is_ordinal a -> is_ordinal b -> order_isomorphic (ordinal_o b) c ->
  a *o b = ord_sum c (cst_graph (substrate c) a).
Proof.
move=> a b c ota otb ibc.
move: (ordinal_p10 ota) => wo0.
move: (ordinal_p10 otb) => wo1.
move: (worder_invariance ibc wo1) => wo2.
rewrite /ord_prod2 /ord_sum.
have ->: domain (cst_graph (substrate c) a) = substrate c.
   rewrite /cst_graph; bw.
apply ordinal_p8.
  by apply order_prod2_worder; apply ordinal_p10.
  apply order_sum_worder; [exact | bw | gprops | bw].
  move=> i isc; rewrite /cst_graph; bw;apply (ordinal_p10 ota).
move: (order_prod_pr (worder_is_order wo0)(worder_is_order wo1)).
set aux := order_sum _ _ .
move=> h; apply orderIT with aux; first by apply h.
move: ibc => [f isf].
rewrite /aux /cst_graph; apply ord_sum_invariant1 with f; bw;gprops; fprops.
move: isf => [_ [_ [[[ff _]] _] [sf [tf _]]]].
rewrite sf; move=> i id.
move: (inc_W_target ff id); rewrite -tf=> vt.
bw; apply orderIR; fprops.
Qed.

Lemma ord_sum_emptyset: forall r x,
  order r -> substrate r = domain x -> fgraph x ->
  substrate r = emptyset ->
  ord_sum r x = 0o.
Proof.
move=> r x or sr fgx sre.
have oa:order_sum_a r (L (domain x) (fun z : Set => ordinal_o (V z x))).
  by red;ee;bw; rewrite -sr sre; move => i ie; elim (emptyset_pr ie).
rewrite /ord_sum; apply ordinal0_pr1.
  by apply order_sum_order.
rewrite order_sum_substrate //.
apply is_emptyset; move=> t ts; move: (du_index_pr1 ts).
rewrite -sr sre L_domain; move => [qe _]; elim (emptyset_pr qe).
Qed.

Lemma ord_prod_emptyset: forall r x,
  worder r -> substrate r = domain x -> fgraph x ->
  substrate r = emptyset ->
  ord_prod r x = 1o.
Proof.
move=> r x wor sr fgx sre.
have la: (lexicographic_order_axioms r (L (domain x)
   (fun z => ordinal_o (V z x)))).
  red; bw;ee; rewrite -sr sre; move=> i idx; elim (emptyset_pr idx).
rewrite /ord_prod; apply singleton_ordinal.
apply order_prod_order; first by red; ee.
rewrite lexorder_substrate // /prod_of_substrates /fam_of_substrates -sr sre.
rewrite trivial_fgraph emptyset_domain trivial_fgraph product_trivial.
by exists emptyset.
Qed.

Lemma worder_singleton1: forall r e,
 order r -> substrate r = singleton e -> worder r.
Proof.
move=> r e or sr.
have aux: r = singleton (J e e).
  set_extens t; aw.
    move=> tr.
    have tp: t = J (P t) (Q t) by aw; rewrite (order_is_graph or tr).
    rewrite tp in tr |- *.
    move: (inc_arg1_substrate tr) (inc_arg2_substrate tr).
    by rewrite sr !singleton_rw; move=> -> ->.
  move=> ->; order_tac; rewrite sr; aw.
move: (singleton_worder e) => [_ h]; ue.
Qed.

Lemma ord_sum_singleton: forall r x e,
  order r -> substrate r = domain x -> fgraph x ->
  substrate r = singleton e -> is_ordinal (V e x) ->
  ord_sum r x = V e x.
Proof.
move=> r x e or sr fgf srs otve.
rewrite sr in srs.
have edx: (inc e (domain x)) by rewrite srs; fprops.
rewrite srs in sr;move: (worder_singleton1 or sr) => wor.
rewrite /ord_sum.
move: (ordinal_p10 otve) => ove.
set g := L _ _.
have osa: order_sum_a r g.
   rewrite /g; red;ee; first by bw; ue.
   bw; rewrite srs; move=> i idx; bw; awi idx; rewrite idx; fprops.
have wos: worder(order_sum r g).
  apply order_sum_worder=> //.
      rewrite /g; bw; ue.
    rewrite /g; gprops.
  rewrite /g; bw; rewrite srs; move=> i idx; bw; awi idx; rewrite idx; fprops.
apply (ordinal_p9 wos otve).
set (E:= substrate (order_sum r g)); set (F := substrate (ordinal_o (V e x))).
have He:E =sum_of_substrates g by rewrite /E order_sum_substrate //.
have Hf:F = V e x by rewrite /F; apply (ordinal_p7 otve).
have uE: (forall u, inc u E = (is_pair u & Q u = e & inc (P u) F)).
  move=> u; rewrite He /g; apply iff_eq.
    move=> us; move: (du_index_pr1 us).
    rewrite L_domain; move=> [Qu]; bw.
    rewrite srs in Qu; awi Qu; rewrite Qu; ee.
  move => [pu [Qu Pu]].
  have uJ: (u = J (P u) (Q u)) by aw.
  by rewrite uJ Qu; apply inc_disjoint_union1; bw.
have ta: (transf_axioms P E F).
  move=> t; rewrite uE; intuition.
exists (BL P E F).
red; ee; [ | symmetry ;aw | symmetry; aw |].
  apply bl_bijective => //.
    move=> u v; rewrite uE uE; aw; move=> [pu [Qu Pu]][pv [Qv Pv]] sp.
    apply pair_exten =>//; ue.
  move=> y ys; exists (J y e); rewrite uE; aw; ee.
rewrite bl_source;move=> a b aE bE; rewrite ! bl_W //.
move: (aE)(bE); rewrite {1} uE {1} uE; move=> [pu [Qu Pu]][pv [Qv Pv]].
rewrite (order_sum_gle osa) /order_sum_r -He -Hf Qu Qv.
rewrite /g Hf /glt; bw; apply iff_eq; intuition.
Qed.

Lemma ord_prod_singleton: forall r x e,
  order r -> substrate r = domain x -> fgraph x ->
  substrate r = singleton e -> is_ordinal (V e x) ->
  ord_prod r x = V e x.
Proof.
move=> r x e or sr fgf srs otve.
rewrite sr in srs.
have edx: (inc e (domain x)) by rewrite srs; fprops.
rewrite srs in sr;move: (worder_singleton1 or sr) => wor.
rewrite /ord_prod.
move: (ordinal_p10 otve) => ove.
set g := L _ _.
have la: (lexicographic_order_axioms r g).
  rewrite /g; red; ee; first by bw; ue.
  bw; move=> i; rewrite srs=> id; bw; aw; fprops.
move: (lexorder_order la) => ora.
have wos: worder(order_prod r g).
  apply order_prod_worder=> //.
       rewrite /g; bw;ue.
    rewrite /g; gprops.
  rewrite /g; bw; rewrite srs; move=> i idx; bw; awi idx; rewrite idx; fprops.
  rewrite sr; apply singleton_finite.
apply (ordinal_p9 wos otve).
move: (lexorder_substrate la).
set E:= substrate (lexicographic_order r g).
set F := substrate (ordinal_o (V e x)).
have Hf:F = V e x by rewrite /F; apply (ordinal_p7 otve).
have Hf': F = substrate (V e g) by rewrite /g; bw.
move=> slo; move: (slo).
rewrite /prod_of_substrates/fam_of_substrates=> slo'.
have dg: domain g = singleton e by rewrite /g ; bw.
have eg: inc e (domain g); rewrite /g ; bw.
have ta: transf_axioms (V e) E F.
  move=> t; rewrite slo Hf'; apply (prod_of_substrates_pr eg).
have Hc:forall a b, inc a E-> inc b E -> V e a = V e b -> a = b.
  have fg: (fgraph (L (singleton e) (fun i => substrate (V i g)))) by gprops.
  move=> a b; rewrite slo' dg => aE bE sV.
  by apply (productb_exten fg aE bE); bw; move=> i; aw; move => ->.
exists (BL (V e) E F).
red; rewrite bl_source bl_target; ee.
  apply bl_bijective =>//.
  move=> y ys; exists (L (singleton e) (fun _:Set => y)).
  rewrite slo' dg ; aw; bw; ee.
  by move=> i ie; bw; awi ie; rewrite ie -Hf'.
move=> a b aE bE; rewrite bl_W // bl_W //.
rewrite order_prod_gle // -slo; apply iff_eq.
  move=> [_ [_ hyp]]; case hyp.
     move: (worder_is_order ove) => orve.
     move=> ->; order_tac.
     move: bE; rewrite slo'; aw; bw; last by gprops.
     move=> [fgb [db aux]].
     have ed: inc e (domain b) by rewrite db dg; aw.
     by move: (aux _ ed); rewrite /g;bw; rewrite -/F Hf.
  rewrite sr srs;move=> [j [jsr]]; bw.
  by awi jsr; rewrite jsr; move=> [[jle _] _].
rewrite sr.
move=> h.
case (equal_or_not a b) => ab; ee.
right; exists e; ee.
  bw; split => //; first by dneg nab; apply Hc =>//.
move=> i [lie ine].
have : inc i (substrate r) by order_tac.
rewrite sr; aw=> h'; elim ine; auto.
Qed.

Lemma zero_unit_sum_ord1: forall r g j,
  order_sum_a r g -> sub j (domain g) ->
  (forall i, inc i (complement (domain g) j) -> V i g = emptyset) ->
  (order_sum r g) \Is (order_sum (induced_order r j) (restr g j)).
Proof.
move=> r g j oa sj alz.
move: (oa) => [or [sr [fgg alo]]].
have dr: (domain (restr g j)) = j by rewrite restr_domain1.
have sj1: sub j (substrate r) by ue.
have oa': order_sum_a (induced_order r j) (restr g j).
  red; aw;ee;rewrite dr;move=> i id;rewrite restr_ev //.
  apply (alo _ (sj _ id)).
have o1: order (order_sum r g) by fprops.
have o2: order (order_sum (induced_order r j) (restr g j)) by fprops.
set E := substrate (order_sum r g).
set F := substrate (order_sum (induced_order r j) (restr g j)).
have EF: E = F.
  set_extens z; rewrite /E/F; bw.
    move => zs; move: (du_index_pr1 zs) => [Qd [Pd pz]].
    have ->: z = J (P z) (Q z) by aw.
    case (p_or_not_p (inc (Q z) (domain (restr g j)))) => aux.
    apply inc_disjoint_union1 => //; rewrite restr_ev //; ue.
    have Qx: inc (Q z) (complement (domain g) j) by srw; ee;ue.
    move: (alz _ Qx) => Ve.
    rewrite Ve emptyset_substrate in Pd; elim (emptyset_pr Pd).
  move => zs; move: (du_index_pr1 zs) => [Qd [Pd pz]].
  have ->: z = J (P z) (Q z) by aw.
  apply inc_disjoint_union1 => //.
     rewrite -sr; apply sj1; ue.
  move: Pd; rewrite restr_ev //; ue.
exists (identity E).
red; rewrite identity_source; ee.
    apply identity_bijective.
  rewrite identity_target //.
have a1: forall x, inc x F -> inc (Q x) j.
   move=> x; rewrite /F; bw => xs; move: (du_index_pr1 xs)=> [aux _]; ue.
move=> x y xE yE; rewrite identity_W // identity_W //.
rewrite (order_sum_gle oa) (order_sum_gle oa').
have <-: E = (sum_of_substrates g) by rewrite /E; bw.
have <-: F = (sum_of_substrates (restr g j)) by rewrite /F; bw.
rewrite /order_sum_r -EF /glt.
have <-: (gle (induced_order r j) (Q x) (Q y) = (gle r (Q x) (Q y))).
   rewrite /gle/related/induced_order; apply iff_eq; aw; first by move=> [ok _].
   move=> h; ee; rewrite /coarse; aw; ee; apply a1; ue.
have ->: V (Q x) (restr g j) = (V (Q x) g).
  rewrite restr_ev //; apply a1; ue.
done.
Qed.

Lemma zero_unit_sum_ord2: forall r g j,
  worder r -> substrate r = domain g -> fgraph g ->
  (forall i, inc i (domain g) -> is_ordinal (V i g)) ->
  sub j (domain g) ->
  (forall i, inc i (complement (domain g) j) -> V i g = 0o) ->
  ord_sum r g = ord_sum (induced_order r j) (restr g j).
Proof.
move=> r g j wor sr fgg albo jg alz.
have jsr: sub j (substrate r) by ue.
have or: order r by fprops.
have jd: j = domain (restr g j) by rewrite restr_domain1.
rewrite /ord_sum; apply ordinal_p8.
   apply order_sum_worder; [ exact | bw | gprops | bw ].
   by move => i idg; bw; apply ordinal_p10; apply albo.
 apply order_sum_worder; [ apply worder_restriction => // | bw; aw |gprops |]. rewrite -jd; bw; move=> i idg; bw; apply ordinal_p10; apply albo.
 by apply jg.
set g1:= (L (domain g) (fun z : Set => ordinal_o (V z g))).
have ->: (L (domain (restr g j)) (fun z : Set => ordinal_o (V z (restr g j)))
  = (restr g1 j)).
  rewrite /g1; apply fgraph_exten.
    - gprops.
    - apply restr_fgraph; gprops.
    - bw; rewrite -jd restr_domain1 //; gprops; bw.
    - bw; rewrite -jd => x xd; bw; [ by apply jg |gprops].
have dg1: domain g1 = domain g by rewrite /g1; bw.
apply zero_unit_sum_ord1.
    split; first by exact.
    split; first by ue.
    split; first rewrite /g1; gprops.
    rewrite dg1; move=> i idg; rewrite /g1; bw.
    apply (ordinal_p11 (albo _ idg)).
  ue.
rewrite dg1; move=> i ic. rewrite /g1; bw.
  rewrite (alz _ ic); apply empty_substrate_zero.
    apply (ordinal_p11 ordinal_0).
    by rewrite (ordinal_p7 ordinal_0).
by move: ic; srw; move => [ok _].
Qed.

Lemma one_unit_prod_ord1: forall r g j,
  order_prod_a r g -> sub j (domain g) ->
  (forall i, inc i (complement (domain g) j)
     -> is_singleton (substrate (V i g))) ->
  (order_prod r g) \Is (order_prod (induced_order r j) (restr g j)).
Proof.
move=> r g j oa sj alz.
move: (oa) => [or [sr [fgg alo]]].
have dr: (domain (restr g j)) = j by rewrite restr_domain1.
have sj1: sub j (substrate r) by ue.
have oa': order_prod_a (induced_order r j) (restr g j).
  red; red;aw; ee; first by apply worder_restriction => //.
  rewrite dr;move=> i id;rewrite restr_ev //; apply (alo _ (sj _ id)).
have o1: order (order_prod r g) by fprops.
have o2: order (order_prod (induced_order r j) (restr g j)) by fprops.
set E := substrate (order_prod r g).
set F := substrate (order_prod (induced_order r j) (restr g j)).
have Ep: E = prod_of_substrates g by rewrite /E ; bw.
have Fp: F = prod_of_substrates (restr g j)by rewrite /F; bw.
have fg1: fgraph (fam_of_substrates (restr g j))
   by rewrite /fam_of_substrates ;gprops.
have fg2: fgraph (fam_of_substrates g) by rewrite /fam_of_substrates ;gprops.
have dg1: domain (fam_of_substrates g) = domain g
    by rewrite /fam_of_substrates; bw.
have dgr: domain (fam_of_substrates (restr g j)) = j.
  rewrite /fam_of_substrates restr_domain1 //; bw.
have rd: j = domain (restr g j) by rewrite restr_domain1 //.
have ta: transf_axioms (fun z => restr z j) E F.
   move=> z; rewrite Ep Fp /prod_of_substrates.
   aw; move=> [fgz [dz iVV]]; rewrite dg1 in dz.
   have sj2: sub j (domain z) by ue.
   ee; first by rewrite dgr restr_domain1 //; bw.
   move=> i; rewrite restr_domain1 // => ij.
   rewrite /fam_of_substrates //; bw; last by ue.
   move: (iVV _ (sj2 _ ij)); rewrite /fam_of_substrates; bw.
   by rewrite -dz; apply sj2.
have specs: forall i x y, inc i (domain g) -> ~ inc i j ->
  domain x = domain (fam_of_substrates g) ->
  domain y = domain (fam_of_substrates g) ->
  (forall i, inc i (domain x) -> inc (V i x) (V i (fam_of_substrates g))) ->
  (forall i, inc i (domain y) -> inc (V i y) (V i (fam_of_substrates g))) ->
  V i x = V i y.
  move=> i x y idg nij dx dy p1 p2.
  have xc: (inc i (complement (domain g) j)) by srw; ee.
  have xdu: inc i (domain x).
     by move: dx; rewrite /fam_of_substrates ; bw => ->.
   have xdv: inc i (domain y).
     by move: dy; rewrite /fam_of_substrates; bw => ->.
  move: (p1 _ xdu);rewrite /fam_of_substrates; bw => h1.
  move: (p2 _ xdv);rewrite /fam_of_substrates; bw => h2.
  by move: (alz _ xc); rewrite is_singleton_rw; move=> [_]; apply.
have ri: forall u v, inc u E -> inc v E -> restr u j = restr v j -> u = v.
  move=> u v uE vE; move: (uE)(vE); rewrite Ep /prod_of_substrates; aw.
  move=> [fgu [du up]][fgv [dv vp]] sar.
  apply fgraph_exten => //; first by ue.
  have sj3: sub j (domain u) by rewrite du dg1.
  have sj4: sub j (domain v) by rewrite dv dg1.
  rewrite du dg1; move=> x xdg; case (inc_or_not x j).
    move=> xj; move: (f_equal (V x) sar); rewrite restr_ev// restr_ev//.
  move=> nxj.
  apply specs => //.
exists (BL (fun z => restr z j) E F).
red; rewrite bl_source bl_target; ee.
  apply bl_bijective => //.
  move=> y; rewrite Ep Fp /prod_of_substrates; aw.
  move=> [fgy [dy iVV]].
  set x := (L (domain g)
    (fun z => Yo (inc z j) (V z y) (rep (substrate (V z g))))).
  have fgx: fgraph x by rewrite /x; gprops.
  have dx: domain x = domain g by rewrite /x; bw.
  exists x; split.
    aw; rewrite dx; ee; move=> i idx; rewrite /fam_of_substrates; bw.
    rewrite /x; bw; Ytac1 xj.
      rewrite dy dgr in iVV; move: (iVV _ xj); bw.
      rewrite /fam_of_substrates; bw; ue.
    apply nonempty_rep.
    have xc: (inc i (complement (domain g) j)) by srw; ee.
    by move: (alz _ xc); rewrite is_singleton_rw; intuition.
  have sj3: sub j (domain x) by ue.
  have dr3: domain (restr x j) = domain y by rewrite restr_domain1 // dy dgr.
  apply fgraph_exten => //; fprops.
  move=> i; rewrite dr3 dy dgr => ij; rewrite restr_ev //.
  by rewrite /x; bw; [ Ytac0 | apply sj].
have sjr: sub j (substrate r) by ue.
have odr: order r by move: or => [or _].
move=> x y xE yE; rewrite bl_W // bl_W//.
rewrite order_prod_gle // order_prod_gle // -Fp -Ep.
have ->: substrate (induced_order r j) = j by rewrite substrate_induced_order.
move: (xE)(yE); rewrite Ep /prod_of_substrates; aw.
move=> [fgx [dx iVVx]] [fgy [dy iVVy]].
have sjx: sub j (domain x) by rewrite dx dg1.
have sjy: sub j (domain y) by rewrite dy dg1.
have raux: forall k, inc k j ->
    glt (V k (restr g j)) (V k (restr x j)) (V k (restr y j))=
    glt (V k g) (V k x) (V k y).
  move=> k kj; rewrite restr_ev // restr_ev // restr_ev //.
apply iff_eq; move=> [_ [_ hyp]].
  split; first( by apply ta); split; first by apply ta.
  case hyp; first by move=> -> ; left.
  move => [k [ksr [klt kle]]]; right; exists k.
  case (inc_or_not k j) => kj.
    ee; first by rewrite raux.
    move => i ilt.
    move: (iorder_gle4 ilt) => [ij _].
    rewrite restr_ev // restr_ev //.
    apply (kle _ (iorder_gle2 ilt)).
  rewrite sr in ksr; move: klt => [_ bad]; elim bad.
  apply specs => //.
split => //; split => //; case hyp; first by move => aux; left; apply ri.
move => [k [ksr [klt kle]]]; right; exists k.
rewrite sr; split; first by apply sj.
split; first by rewrite -raux.
move=> i ik; case (inc_or_not i j) => ij.
  have lik: glt (induced_order r j) i k by rewrite /glt; aw.
  move: (kle _ lik); rewrite restr_ev// restr_ev//.
have isr: inc i (domain g) by rewrite -sr;order_tac.
apply specs => //.
Qed.

Lemma one_unit_prod_ord2: forall r g j,
  worder r -> substrate r = domain g -> fgraph g ->
  (forall i, inc i (domain g) -> is_ordinal (V i g)) ->
  finite_set (substrate r) ->
  sub j (domain g) ->
  (forall i, inc i (complement (domain g) j) -> V i g = 1o) ->
  ord_prod r g = ord_prod (induced_order r j) (restr g j).
Proof.
move=> r g j wor sr fgg albo fs jg alz.
have jsr: sub j (substrate r) by ue.
have or: order r by fprops.
have jd: j = domain (restr g j) by rewrite restr_domain1.
rewrite /ord_prod; apply ordinal_p8.
    apply order_prod_worder; [ exact | bw | gprops | bw | gprops ].
    by move => i idg; bw; apply ordinal_p10; apply albo.
  apply order_prod_worder; gprops.
        apply worder_restriction => //.
      bw; aw.
    rewrite -jd; bw; move=> i idg; bw; apply ordinal_p10; apply albo.
    by apply jg.
  aw; apply (sub_finite_set jsr fs).
set g1:= (L (domain g) (fun z : Set => ordinal_o (V z g))).
have ->: (L (domain (restr g j)) (fun z : Set => ordinal_o (V z (restr g j)))
  = (restr g1 j)).
  rewrite /g1; apply fgraph_exten.
    - gprops.
    - apply restr_fgraph; gprops.
    - bw; rewrite -jd restr_domain1 //; gprops; bw.
    - bw; rewrite -jd => x xd; bw; [ by apply jg |gprops].
have dg1: domain g1 = domain g by rewrite /g1; bw.
apply one_unit_prod_ord1.
    split; first by exact.
    split; first by ue.
    split; first rewrite /g1; gprops.
    rewrite dg1; move=> i idg; rewrite /g1; bw.
    apply (ordinal_p11 (albo _ idg)).
  ue.
rewrite dg1; move=> i ic.
rewrite /g1; bw; last by move: ic; srw; move => [ok _].
rewrite (alz _ ic) (ordinal_p7 ordinal_1).
by exists emptyset.
Qed.

Lemma unit_helper: forall x y j,
  let g := (variantLc x y) in
    let r := canonical_doubleton_order in
      is_ordinal x -> is_ordinal y -> sub j (domain g) ->
      (worder r & substrate r = domain g & fgraph g
        & (forall i, inc i (domain g) -> is_ordinal (V i g))
        & worder (induced_order r j)
        & substrate (induced_order r j) = j
        & substrate (induced_order r j) = domain (restr g j)
        & fgraph (restr g j)).
Proof.
move=> x y j g r ox oy sj.
have fgg: fgraph g by rewrite /g;fprops.
have dr: domain (restr g j) = j by rewrite restr_domain1.
have wor: worder r by apply worder_canonical_doubleton_order.
have sr: substrate r = domain g.
   by rewrite substrate_canonical_doubleton_order /g; bw.
have sj1: sub j (substrate r) by rewrite sr.
have sj2: substrate (induced_order r j) = j by aw; fprops.
have q4: (forall i : Set, inc i (domain g) -> is_ordinal (V i g)).
  by rewrite /g; bw; move=> i itp; try_lvariant itp.
ee.
- by apply worder_restriction.
- by rewrite sj2 dr.
Qed.

Lemma ord_0_plus_unit_l: forall x, is_ordinal x ->
  0o +o x = x.
Proof.
move=> x ox.
set g := (variantLc 0o x).
have ->: ord_sum2 0o x = ord_sum canonical_doubleton_order g.
  by rewrite /g /ord_sum2 /order_sum2 /ord_sum variantLc_comp.
set j := singleton TPb.
have sj: sub j (domain g) by rewrite /g/j; move=> t; bw; aw; move=>->; fprops.
move: (unit_helper ordinal_0 ox sj)=> [p1 [p2 [p3 [p4 [p5 [p6 [p7 p8]]]]]]].
have p11: (forall i, inc i (complement (domain g) j) -> V i g = 0o).
  move => i; rewrite /g /j; bw; aw;srw; aw; move=> [itp aux].
  by move: aux; move:itp=> [] ->; bw; move=> h; elim h.
rewrite (zero_unit_sum_ord2 p1 p2 p3 p4 sj p11).
have res: V TPb (restr g j) = x by rewrite /g /j; bw; fprops.
rewrite (ord_sum_singleton (e:= TPb)) //; [ fprops | ue ].
Qed.

Lemma ord_0_plus_unit_r: forall x, is_ordinal x ->
  x +o 0o = x.
Proof.
move=> x ox.
set g := (variantLc x 0o).
have ->: ord_sum2 x 0o = (ord_sum canonical_doubleton_order g).
  by rewrite /ord_sum2 /order_sum2 /ord_sum variantLc_comp.
set j := singleton TPa.
have sj: sub j (domain g) by rewrite /g/j; move=> t; bw; aw; move=>->; fprops.
move: (unit_helper ox ordinal_0 sj)=> [p1 [p2 [p3 [p4 [p5 [p6 [p7 p8]]]]]]].
have p11: (forall i, inc i (complement (domain g) j) -> V i g = 0o).
  move => i; rewrite /g /j; bw; aw;srw; aw; move=> [itp aux].
  by move: aux; move:itp=> [] ->; bw; move=> h; elim h.
rewrite (zero_unit_sum_ord2 p1 p2 p3 p4 sj p11).
have res: V TPa (restr g j) = x by rewrite /g/j; bw; fprops.
rewrite (ord_sum_singleton (e:= TPa)) //; [ fprops | ue ].
Qed.

Lemma ord_1_mult_unit_r: forall x, is_ordinal x ->
  x *o 1o = x.
Proof.
move=> x ox.
set g := (variantLc 1o x).
have ->: ord_prod2 x 1o = (ord_prod canonical_doubleton_order g).
  by rewrite /g/ord_prod2 /order_prod2 /ord_prod variantLc_comp.
set j := singleton TPb.
have fs: finite_set (substrate canonical_doubleton_order).
  rewrite substrate_canonical_doubleton_order.
  rewrite -two_points_pr2; apply doubleton_finite.
have sj: sub j (domain g) by rewrite /g/j; move=> t; bw; aw; move=>->; fprops.
move: (unit_helper ordinal_1 ox sj)=>[p1 [p2 [p3 [p4 [p5 [p6 [p7 p8]]]]]]].
have p11: (forall i, inc i (complement (domain g) j) -> V i g = 1o).
  move => i; rewrite /g /j; bw; aw;srw; aw; move=> [itp aux].
  by move: aux; move:itp=> [] ->; bw; move=> h; elim h.
rewrite (one_unit_prod_ord2 p1 p2 p3 p4 fs sj p11).
have res: V TPb (restr g j) = x by rewrite /g/j; bw; fprops.
rewrite (ord_prod_singleton (e:= TPb)) //; [ fprops | ue ].
Qed.

Lemma ord_1_mult_unit_l: forall x, is_ordinal x ->
  1o *o x = x.
Proof.
move=> x ox.
set g := (variantLc x 1o).
have ->: ord_prod2 1o x= (ord_prod canonical_doubleton_order g).
  by rewrite /g /ord_prod2 /order_prod2 /ord_prod variantLc_comp.
set j := singleton TPa.
have fs: finite_set (substrate canonical_doubleton_order).
  rewrite substrate_canonical_doubleton_order.
  rewrite -two_points_pr2; apply doubleton_finite.
have sj: sub j (domain g) by rewrite /g/j; move=> t; bw; aw; move=>->; fprops.
move: (unit_helper ox ordinal_1 sj)=>[p1 [p2 [p3 [p4 [p5 [p6 [p7 p8]]]]]]].
have p11: (forall i, inc i (complement (domain g) j) -> V i g = 1o).
  move => i; rewrite /g /j; bw; aw;srw; aw; move=> [itp aux].
  by move: aux; move:itp=> [] ->; bw; move=> h; elim h.
rewrite (one_unit_prod_ord2 p1 p2 p3 p4 fs sj p11).
have res: V TPa (restr g j) = x by rewrite /g/j; bw; fprops.
rewrite (ord_prod_singleton (e:= TPa)) //; [ fprops | ue ].
Qed.

Lemma order_sum_assoc_iso: forall r g r' g',
  order_sum_a r g -> order_sum_a r' g' ->
  r = order_sum r' g' ->
  let order_sum_assoc_aux :=
    fun l =>
    order_sum (V l g') (L (substrate (V l g')) (fun i => V (J i l) g)) in
  let order_sum_assoc :=
    order_sum r' (L (domain g') order_sum_assoc_aux)
  in order_isomorphism (BL (fun x=> J (J (P x) (P (Q x))) (Q (Q x)))
      (sum_of_substrates g) (substrate (order_sum_assoc)))
  (order_sum r g) (order_sum_assoc).
Proof.
move=> r g r' g' oa oa' oa_link F G.
move: (oa) => [or [sr [fgg alo]]].
move: (oa') => [or' [sr' [fgg' alo']]].
have alo'' :forall l,
  inc l (substrate r') ->
  order_sum_a (V l g')
  (L (substrate (V l g')) (fun i => V (J i l) g)).
  move=> l ls; red; ee; bw; first by apply alo' =>//; ue.
  move => i isg; bw; apply alo; rewrite -sr oa_link.
  rewrite order_sum_substrate //; apply inc_disjoint_union1 =>// ; ue.
have order_sum_assoc_aux2 :forall l,
  inc l (domain g') -> order (F l).
  move=> l ldg'; rewrite /F; rewrite -sr' in ldg'.
  apply order_sum_order; apply alo'' => //.
have oa'' : order_sum_a r' (L (domain g') (fun l=> F l)).
  by red; bw; ee; move => i idf; bw; apply order_sum_assoc_aux2.
have org : order G by rewrite /G; apply order_sum_order=>//.
have ta: (transf_axioms (fun x => J (J (P x) (P (Q x))) (Q (Q x)))
        (sum_of_substrates g) (substrate G)).
  move=> x xs; rewrite /G; aw; move: (du_index_pr1 xs) => [Qd [Ps px]].
  move: Qd;rewrite -sr oa_link; bw => Qd.
  move: (du_index_pr1 Qd) => [QQd [PQs pQ]].
  apply inc_disjoint_union1; bw.
  rewrite /F;bw; first by apply inc_disjoint_union1; bw; rewrite pQ.
  apply alo''; ue.
set(FF:= BL (fun x => J (J (P x) (P (Q x))) (Q (Q x)))
    (sum_of_substrates g) (substrate G)).
have injF: injection FF.
  apply bl_injective => //.
  move => u v us vs eq; move: (pr1_def eq) (pr2_def eq)=> eq1 eq2.
  move: (pr1_def eq1) (pr2_def eq1) => eq3 eq4.
  move: (du_index_pr1 us) (du_index_pr1 vs) => [Qu [_ pu]][Qv [_ pv]].
  apply pair_exten =>//.
  move: Qu Qv;rewrite -sr oa_link; bw=> Qu Qv.
  move: (du_index_pr1 Qu)(du_index_pr1 Qv) => [_ [_ pqu]][_ [_ pqv]].
  apply pair_exten => //.
have bF: (bijection FF).
  split =>//; apply bl_surjective => //.
  move=> y; rewrite /G; bw => yG.
  move: (du_index_pr1 yG); rewrite L_domain=> [[Qd]]; bw; move=> [Py py].
  rewrite sr' in alo''; move: (alo'' _ Qd) => al.
  move: (Py); rewrite /F; bw => paux.
  move: (du_index_pr1 paux);rewrite L_domain=> [[PQd]]; bw; move=> [PPy Ppy].
  set (x:= J (P (P y)) (J (Q (P y)) (Q y))).
  have xs: (inc x (sum_of_substrates g)).
    rewrite /x;apply inc_disjoint_union1 =>//.
    rewrite -sr oa_link; bw; apply inc_disjoint_union1 =>//.
  by ex_tac; rewrite /x ; aw; rewrite Ppy py.
rewrite /FF;red; rewrite bl_source bl_target; ee; bw.
move=> x y xs ys; rewrite (bl_W ta xs) (bl_W ta ys).
set gx := (J (J (P x) (P (Q x))) (Q (Q x))).
set gy := (J (J (P y) (P (Q y))) (Q (Q y))).
rewrite /G (order_sum_gle oa) (order_sum_gle oa'').
set P1:=inc gx (sum_of_substrates (L (domain g') (fun l : Set => F l))).
set P2:=inc gy (sum_of_substrates (L (domain g') (fun l : Set => F l))).
rewrite /gx /gy /order_sum_r ! pr1_pair ! pr2_pair.
rewrite oa_link; rewrite {1} /glt (order_sum_gle oa') /order_sum_r.
move: (du_index_pr1 xs)(du_index_pr1 ys) => [Qu [Pu pu]][Qv [Pv pv]].
move: Qu Qv;rewrite -sr oa_link (order_sum_substrate oa') => Qu Qv.
move: (du_index_pr1 Qu)(du_index_pr1 Qv) => [QQx [PQx pqu]][QQy [pQy pqv]].
have aux3: (J (P (Q x)) (Q (Q x))) = Q x by aw.
have aux4: (J (P (Q y)) (Q (Q y))) = Q y by aw.
have p1p: P1.
  rewrite /P1; apply inc_disjoint_union1; bw.
  have Qs: inc (Q (Q x)) (substrate r') by ue.
  rewrite /F; bw; try apply alo'' =>//.
  apply inc_disjoint_union1; [ rewrite L_domain // | bw; ue].
have p2p: P2.
  rewrite /P2; apply inc_disjoint_union1; bw.
  have Qs: inc (Q (Q y)) (substrate r') by ue.
  rewrite /F; bw; try apply alo'' =>//.
  apply inc_disjoint_union1; [ rewrite L_domain // | bw; ue].
apply iff_eq.
  have aux1: inc (Q (Q x)) (substrate r') by ue.
  move: (alo'' _ aux1) => aux2.
  move=> hyp; eee; move: hyp => [_ [_ aux]]; case aux.
    move=> [[Qx1 [Qx2 hr]] nxy]; case hr; first by auto.
    move => [aa bb]; right; eee; bw; rewrite /F order_sum_gle //.
    eee.
        apply inc_disjoint_union1; bw; ue.
      rewrite aa;apply inc_disjoint_union1; bw; ue.
   red; rewrite !pr2_pair (L_V_rw _ PQx) !pr1_pair aux3.
   case (equal_or_not (P (Q x)) (P (Q y)))=> aux5; last by left;split.
   elim nxy; rewrite -aux3 -aux4 aa aux5 //.
  move=> [sq le1]; right;rewrite - sq; ee.
  bw;rewrite /F order_sum_gle //.
  eee.
      apply inc_disjoint_union1; bw; ue.
    rewrite sq;apply inc_disjoint_union1; bw; ue.
  red; rewrite !pr2_pair (L_V_rw _ PQx) !pr1_pair aux3; intuition.
move=> [_ [_ aux]]; ee; case aux.
  move=> lt; left; ee.
  move:lt => [_ ne]; dneg ne1; ue.
move=> [qq]; bw.
have Qs: inc (Q (Q x)) (substrate r') by ue.
rewrite /F (order_sum_gle (alo'' _ Qs)).
move=> [p1 [p2]]; rewrite /order_sum_r !pr1_pair !pr2_pair.
case; move => [hyp1 hyp2].
  have aux2: Q x <> Q y by dneg ne1; ue.
  left; ee.
right; ee; first by apply pair_exten =>//.
by move: hyp2; bw;rewrite pqu.
Qed.

Lemma order_prod_assoc_iso: forall r g r' g',
  order_prod_a r g -> order_sum_a r' g' ->
  r = order_sum r' g' ->
  worder r' ->
  (forall i, inc i (domain g') -> worder( V i g')) ->
  let order_sum_assoc_aux :=
    fun l =>
    order_prod (V l g') (L (substrate (V l g')) (fun i => V (J i l) g)) in
  let ordinal_prod_assoc :=
    order_prod r' (L (domain g') order_sum_assoc_aux)
  in order_isomorphism (BL
     (fun z => L (domain g') (fun l =>
       L (substrate (V l g')) (fun j => V (J j l) z)))
      (prod_of_substrates g) (substrate (ordinal_prod_assoc)))
  (order_prod r g) (ordinal_prod_assoc).
Proof.
move=> r g r' g' la oa oa_link wor' alwo F G.
move: (oa) => [or' [sr' [fgg' alo']]].
move: (la) => [wor [sr [fgg alo]]].
have dgpr: forall i j, inc i (domain g') -> inc j (substrate (V i g')) ->
   inc (J j i) (domain g).
  move=> i j id jd; rewrite -sr oa_link; bw; apply inc_disjoint_union1 =>//.
have la1: forall i, inc i (domain g') ->
   order_prod_a (V i g')
     (L (substrate (V i g')) (fun i0 : Set => V (J i0 i) g)).
   move=> i idg; red;red; ee;bw; move=> j js; bw; apply alo; apply dgpr =>//.
have la': order_prod_a r' (L (domain g') F).
  by red; red;ee;bw; move=> i idg'; rewrite /F; bw; fprops.
have oG: order G by rewrite /G; fprops.
have s1: substrate (order_prod r g) = prod_of_substrates g by bw.
have sG1: substrate G = productb (L (domain g') (fun i => substrate (F i))).
  rewrite /G; bw; rewrite /prod_of_substrates /fam_of_substrates; bw.
  apply f_equal; apply L_exten1 =>//; move=> i idg; bw.
have sG0: substrate G = (prod_of_substrates (L (domain g') F)).
  rewrite /G lexorder_substrate // /prod_of_substrates /fam_of_substrates; bw.
have sG2: substrate G = productb
      (L (domain g') (fun i => productb
        (L (substrate (V i g'))(fun i0 : Set => substrate (V (J i0 i) g))))).
  rewrite sG1; apply f_equal; apply L_exten1 =>//.
  move=> i idg; rewrite /F;bw; last by apply la1 =>//.
  rewrite /prod_of_substrates /fam_of_substrates; bw.
  apply f_equal; apply L_exten1 =>//; move=> x xs; bw.
have g1: fgraph (L (domain g) (fun i => substrate (V i g))) by gprops.
have g2: fgraph (L (domain g') (fun i=> substrate (V i (L (domain g') F))))
  by gprops.
set h := (fun z => L (domain g') (fun l =>
     L (substrate (V l g')) (fun j => V (J j l) z))).
have p1: forall z, inc z (prod_of_substrates g) -> inc (h z) (substrate G).
  move=> z zp; move: (zp).
  rewrite /prod_of_substrates /fam_of_substrates; aw; bw.
  move=> [fgz [dz iVV]]; rewrite dz in iVV.
  rewrite sG2 /h; rewrite productb_rw ; last by gprops.
  split; first (by gprops); split; bw.
  move=> i idg; bw; aw; ee; gprops; bw; move=> j js; bw.
  move: (dgpr _ _ idg js) => aux; move: (iVV _ aux); bw; gprops.
have p2: forall a b, inc a (prod_of_substrates g) ->
   inc b (prod_of_substrates g) -> h a = h b -> a = b.
  move=> a b; rewrite /prod_of_substrates /fam_of_substrates; aw; bw.
  rewrite /h; move=> [fga [da iVVa]][fgb [db iVVb]] eqn.
  apply fgraph_exten =>//; try ue; move=> x xda.
  move: xda; rewrite da -sr oa_link; bw => xsr.
  move: (du_index_pr1 xsr) => [Qx [Px px]].
  move: (f_equal (V (Q x)) eqn); bw => eq2.
  by move: (f_equal (V (P x)) eq2); bw; rewrite px.
have p3: forall x, inc x (substrate G) -> exists z,
  (inc z (prod_of_substrates g) & (h z) = x).
  move=> x;rewrite sG2.
  aw; bw; last (by gprops); move=> [fgx [dx iVVx]].
  exists (L (domain g) (fun w => V (P w) (V (Q w) x))); split.
    have aux: product_order_axioms g by red; ee.
    aw; ee; bw; move=> i idg; bw.
    move : idg; rewrite -sr oa_link; bw => xsr.
    move: (du_index_pr1 xsr) => [Qx [Px px]].
    rewrite dx in iVVx; move: (iVVx _ Qx); bw.
    rewrite productb_rw; last by gprops.
    bw; move=> [fg1 [d1 ivv1]];rewrite d1 in ivv1;move: (ivv1 _ Px); bw.
    by have ->: J (P i) (Q i) = i by aw.
  rewrite /h; apply fgraph_exten =>//;[ gprops | bw; ue | bw].
  move=> j jdg; bw; rewrite dx in iVVx; move: (iVVx _ jdg); bw; aw; bw.
    move=> [fgj [dj iVV2]]; apply fgraph_exten; [ gprops | exact | bw; ue | bw].
    move=> w ws; bw; [ aw | apply dgpr =>//].
  gprops.
red; ee; aw.
  apply bl_bijective; ee.
move=> a b ap bp.
rewrite order_prod_gle // order_prod_gle //.
rewrite bl_W // bl_W //.
set P1:= inc (h a) _; set P2:= inc (h b) _.
have P1p: P1 by rewrite /P1; move: (p1 _ ap); rewrite sG0 //.
have P2p: P2 by rewrite /P2; move: (p1 _ bp); rewrite sG0 //.
apply iff_eq; move=> [p4 [p5 cmp]]; eee.
  case cmp; first by move=> ->; left.
  move=> [j [jsr [[jlt diff] jle]]]; right.
  move: jsr; rewrite oa_link; bw => jsg'.
  move: (du_index_pr1 jsg') => [Qx [Px px]].
  rewrite sr';ex_tac; split.
    have paux: forall c, inc c (prod_of_substrates g) ->
      inc (V (Q j) (h c)) (prod_of_substrates
        (L (substrate (V (Q j) g')) (fun i : Set => V (J i (Q j)) g))).
      move=> i ic;move: (p1 _ ic); rewrite sG1.
      rewrite productb_rw; last by gprops.
      bw;move=> [fgh [dh ivv]]; rewrite dh in ivv; move: (ivv _ Qx); bw.
      rewrite /F lexorder_substrate //; apply la1 => //.
    bw; rewrite /F ;split.
      rewrite order_prod_gle //; last (by apply la1); ee.
      right; ex_tac; bw; split.
        rewrite /h; bw; by have ->: (J (P j) (Q j)) = j by aw.
      move=> i [ile ne].
      have isr: inc i (substrate (V (Q j) g')) by order_tac.
      rewrite /h; bw; apply jle; split; last by dneg aa; rewrite -aa; aw.
      rewrite oa_link order_sum_gle //; ee.
        apply inc_disjoint_union1=> //.
      red; eee; rewrite !pr1_pair !pr2_pair;right ; intuition.
    rewrite /h; bw; dneg aa; move: (f_equal (V (P j)) aa);bw.
    rewrite px //.
  move=> i ilt.
  have idg: inc i (domain g') by rewrite -sr'; order_tac.
  rewrite /h; bw; apply L_exten1 =>// k ks.
  apply jle; split; last by move: ilt => [_ bad]; dneg aa; rewrite -aa; aw.
  rewrite oa_link order_sum_gle //; ee.
    apply inc_disjoint_union1=> //.
  red; aw; intuition.
case cmp; first by move=> eq; left; apply p2.
move=> [j [jsr [jlt jle]]]; right.
rewrite sr' in jsr; move: jlt; rewrite /h /F; bw; move=> [le ns].
move: le; rewrite order_prod_gle //; last by apply la1.
move=> [p6 [p7]];case; first by move=> p8; elim ns.
move=> [k [ks [klt kle]]].
rewrite sr; move: (dgpr _ _ jsr ks) => pd.
ex_tac; split; first by move: klt; bw.
move=> i [lei nei]; move: lei;rewrite oa_link=> lti.
have : inc i (substrate (order_sum r' g')) by order_tac.
bw=> iso; move: (du_index_pr1 iso) => [Qdg [Psg' pi]].
move: (order_sum_gle1 oa lti); aw; case => ch.
  move: (jle _ ch); rewrite /h; bw => eq1.
  by move: (f_equal (V (P i)) eq1); bw; rewrite pi.
move: ch => [Qi aux].
have ->: i = J (P i) j by rewrite -Qi; aw.
have aux2: glt (V j g') (P i) k.
   rewrite -Qi; split => //; dneg aa; rewrite -aa - Qi pi; aw.
rewrite Qi in Psg'; move: (kle _ aux2); bw.
Qed.

Lemma order_sum_assoc1: forall r g r' g',
  worder r -> substrate r = domain g ->
  fgraph g -> (forall i, inc i (domain g) -> is_ordinal (V i g)) ->
  worder r' -> substrate r' = domain g' ->
  fgraph g' -> (forall i, inc i (domain g') -> worder (V i g')) ->
  r = order_sum r' g' ->
  let order_sum_assoc_aux :=
    fun l =>
    ord_sum (V l g') (L (substrate (V l g')) (fun i => V (J i l) g)) in
  ord_sum r g = ord_sum r' (L (domain g') (order_sum_assoc_aux)).
Proof.
move=> r g r' g' wor sr fgf alB wor' sr' fgf' alw oal osa.
rewrite /ord_sum.
have oa': order_sum_a r' g' by red; ee.
apply ordinal_p8.
    apply order_sum_worder => //; [ bw | gprops | bw].
    by move=> i idg; bw; apply ordinal_p10; apply alB.
  apply order_sum_worder => //; [ bw | gprops | bw ].
  move=> i idg'; bw; apply ordinal_p10.
  rewrite /osa; apply ord_sum_ordinal => //; [ by apply alw | bw | gprops| bw].
  move => j js; bw; apply alB; rewrite -sr oal (order_sum_substrate oa').
  by apply inc_disjoint_union1.
set g'' := (L (domain g) (fun z => ordinal_o (V z g))).
have oa: order_sum_a r g''.
  rewrite /g'';red; bw;ee; move=> i idg; bw.
  apply (ordinal_p11 (alB _ idg)).
move: (order_sum_assoc_iso oa oa' oal) => /=.
set aux' := fun l =>
    order_sum (V l g') (L (substrate (V l g')) (fun i => V (J i l) g'')).
set f:= BL _ _ _.
move => oi1; move: (oi1) => [o1 [o2 _]].
apply orderIT with (order_sum r' (L (domain g') aux')); first by exists f; ee.
apply ord_sum_invariant2 =>//; bw; gprops; first by fprops.
rewrite sr';move=> i isr; bw; rewrite /osa /ord_sum; bw.
set s:= order_sum _ _.
have -> : (aux' i = s).
  rewrite /aux' /s. apply f_equal.
  apply L_exten1 => //;move=> j js.
  have pd: inc (J j i) (domain g).
    by rewrite -sr oal (order_sum_substrate oa'); by apply inc_disjoint_union1.
  rewrite /g''; bw.
apply ordinal_p2.
rewrite /s.
apply order_sum_worder; [by apply alw | bw | gprops | bw ].
move=> j js; bw; apply ordinal_p10; apply alB.
rewrite -sr oal (order_sum_substrate oa'); by apply inc_disjoint_union1.
Qed.

Lemma ordinal_prod_assoc1: forall r g r' g',
  worder r -> substrate r = domain g ->
  fgraph g -> (forall i, inc i (domain g) -> is_ordinal (V i g)) ->
  worder r' -> substrate r' = domain g' ->
  fgraph g' -> (forall i, inc i (domain g') -> worder (V i g')) ->
  r = order_sum r' g' ->
  finite_set (substrate r) ->finite_set (substrate r') ->
  (forall i, inc i (domain g') -> finite_set (substrate (V i g'))) ->
  let order_prod_assoc_aux :=
    fun l =>
    ord_prod (V l g') (L (substrate (V l g')) (fun i => V (J i l) g)) in
  ord_prod r g = ord_prod r' (L (domain g') order_prod_assoc_aux).
Proof.
move=> r g r' g' wor sr fgf alB wor' sr' fgf' alw oal fs fs' alfs osa.
rewrite /ord_prod.
have oa': order_sum_a r' g' by red; ee; red; ee.
apply ordinal_p8.
    apply order_prod_worder => //; [ bw | gprops | bw].
    by move=> i idg; bw; apply ordinal_p10; apply alB.
  apply order_prod_worder => //; [ bw | gprops | bw ].
  move=> i idg'; bw; apply ordinal_p10.
  rewrite /osa; apply ord_prod_ordinal; [ by apply alw | bw | gprops| bw |].
    move => j js; bw; apply alB; rewrite -sr oal (order_sum_substrate oa').
    by apply inc_disjoint_union1.
  by apply alfs.
set g'' := (L (domain g) (fun z => ordinal_o (V z g))).
have oa: order_prod_a r g''.
  rewrite /g'';red; red; bw;ee; move=> i idg; bw.
  apply (ordinal_p11 (alB _ idg)).
move: (order_prod_assoc_iso oa oa' oal wor' alw) => /=.
set aux' := fun l =>
    order_prod (V l g') (L (substrate (V l g')) (fun i => V (J i l) g'')).
set f:= BL _ _ _.
move => oi1; move: (oi1) => [o1 [o2 _]].
apply orderIT with (order_prod r' (L (domain g') aux'));
    first by exists f; ee.
apply ord_prod_invariant2 =>//; bw; gprops.
rewrite sr';move=> i isr; bw; rewrite /osa /ord_prod; bw.
set s:= order_prod _ _.
have -> : (aux' i = s).
  rewrite /aux' /s; apply f_equal.
  apply L_exten1 => //;move=> j js.
  have pd: inc (J j i) (domain g).
    by rewrite -sr oal (order_sum_substrate oa'); by apply inc_disjoint_union1.
  rewrite /g''; bw.
apply ordinal_p2.
rewrite /s.
apply order_prod_worder; [by apply alw | bw | gprops | bw | fprops].
move=> j js; bw; apply ordinal_p10; apply alB.
rewrite -sr oal (order_sum_substrate oa'); by apply inc_disjoint_union1.
Qed.

Lemma order_sum_assoc2: forall a b c,
  order a -> order b -> order c ->
  (order_sum2 a (order_sum2 b c))
  \Is (order_sum2 (order_sum2 a b) c).
Proof.
move=> a b c oa ob oc.
have oab: order (order_sum2 a b) by apply order_sum2_order.
have obc: order (order_sum2 b c) by apply order_sum2_order.
set E:= substrate (order_sum2 a (order_sum2 b c)).
set F:= substrate (order_sum2 (order_sum2 a b) c).
set A:= substrate a; set B := substrate b; set C := substrate c.
have Ev: E = canonical_du2 A (canonical_du2 B C).
  by rewrite /E ! order_sum2_substrate.
have Fv: F = canonical_du2 (canonical_du2 A B) C.
  by rewrite /F ! order_sum2_substrate.
move: two_points_distinctb => nT.
move: two_points_distinct => nT1.
set f := fun z => Yo (Q z = TPa) (J (J (P z) TPa) TPa)
          (Yo (Q (P z) = TPa) (J (J (P (P z)) TPb) TPa) (J (P (P z)) TPb)).
have p1: forall x, inc x E -> inc (f x) F.
  move => x; rewrite Ev Fv ! canonical_du2_pr.
  move=> [px]; case.
   move=> [PA Qa]; rewrite /f; Ytac0; split;first by fprops.
   aw; ee.
  move=> [[pPx alt] Qb].
  have nQa: Q x <> TPa by rewrite Qb.
  rewrite /f; Ytac0.
  case alt;move => [Ppx Qpx]; first by Ytac0; aw; ee.
  rewrite (Y_if_not_rw); aw; ee; rewrite Qpx //.
have p2: forall y, inc y F -> exists x, (inc x E & f x = y).
  move => y; rewrite Ev Fv ! canonical_du2_pr; move=> [py]; case.
    move=> [[pPy aux] Qpy]; case aux.
      move=> [PPa QP]; exists (J (P (P y)) TPa); split.
        rewrite canonical_du2_pr;aw; ee.
      rewrite /f pr2_pair; Ytac0; aw.
      apply pair_exten =>//; fprops; aw; try ue.
    move=> [PPy QPy]; exists (J (J (P (P y)) TPa) TPb); split.
      rewrite ! canonical_du2_pr;aw; ee.
    rewrite /f !(pr1_pair, pr2_pair); Ytac0; Ytac0.
    apply pair_exten =>//; fprops; aw; try ue.
  move=> [Py Qy]; exists (J (J (P y) TPb) TPb); split.
   rewrite ! canonical_du2_pr;aw; ee.
   rewrite /f !(pr2_pair, pr1_pair); Ytac0; Ytac0.
   apply pair_exten =>//;fprops; aw;ue.
have p3: forall x y, inc x E -> inc y E -> f x = f y -> x = y.
  move => x y; rewrite Ev ! canonical_du2_pr /f.
  move => [px hx][py hy].
  case hx.
    move => [PxA Qxa]; Ytac0.
    case hy.
      move => [PyA Qya]; Ytac0 => sf; move:(pr1_def sf).
      by rewrite - {1} Qxa - Qya px py.
    move=> [_ ] Qy; rewrite Qy; Ytac0; Ytac h1.
      move => h2;elim nT1; exact: (pr2_def(pr1_def h2)).
       move => h2;elim nT1; exact: (pr2_def h2).
   move=> [[pPx auxx] Qx]; rewrite Qx; Ytac0.
   case hy.
     move=> [_ Qy]; Ytac0; Ytac h.
       move => h2;elim nT; exact: (pr2_def(pr1_def h2)).
     move => h2;elim nT; exact: (pr2_def h2).
  move=> [[pPy auxy] Qy]; rewrite Qy; Ytac0.
  case auxx; move=> [PPx Qpx].
    Ytac0.
      case auxy; move=> [PPy Qpy].
         Ytac0 => sf; move: (pr1_def (pr1_def sf)) => sf1.
         apply pair_exten =>//; try ue.
         apply pair_exten =>//; try ue.
    rewrite Qpy;Ytac0=> sf; elim nT1; exact: (pr2_def sf).
  case auxy; move=> [_ Qpy].
     rewrite Qpx Qpy; Ytac0; Ytac0 => sf; elim nT; exact: (pr2_def sf).
  rewrite Qpx Qpy; Ytac0; Ytac0.
  move=> sf; move: (pr1_def sf) => sf1.
  apply pair_exten =>//; try ue.
  apply pair_exten =>//; try ue.
exists (BL f E F); red; ee; aw.
  apply bl_bijective => //.
move=> x y xE yE;
rewrite bl_W // bl_W// ! order_sum2_gle // !order_sum2_substrate //.
rewrite -/A -/B -/C - Ev -Fv.
apply iff_eq.
  move=> [_ [_ hyp]]; split; first by apply (p1 _ xE).
  split; first by apply (p1 _ yE).
  move: xE; rewrite Ev canonical_du2_pr; move=> [px hx].
  move: yE; rewrite Ev canonical_du2_pr; move=> [py hy].
  case hyp.
    move=> [Qx [Qy le]]; left; rewrite /f (Y_if_rw Qx) (Y_if_rw Qy).
    rewrite ! pr1_pair ! pr2_pair.
    split ; first (by []);split ; first (by []).
    split.
       case hx; move => [hxa hxb].
         rewrite canonical_du2_pr; aw; split; [fprops | left ; split =>//].
         elim nT; by rewrite -Qx -hxb.
    split.
       case hy; move => [hxa hxb].
         rewrite canonical_du2_pr; aw; split; [fprops | left ; split =>//].
         elim nT; by rewrite -Qy -hxb.
    by left; split; [| split ].
  case; last first.
    move=> [Qxa Qyb].
    have fx: f x = (J (J (P x) TPa) TPa) by rewrite /f Y_if_rw //.
    rewrite fx ! pr1_pair ! pr2_pair.
    case hy; first by move=> [_ bad]; contradiction.
    move=> [Py Qy].
    have: f y = (Yo (Q (P y) = TPa)(J (J (P (P y)) TPb) TPa) (J (P (P y)) TPb)).
      by rewrite /f Y_if_not_rw //.
     move: Py; rewrite canonical_du2_pr; move=> [pPy]; case.
       move=> [PPy QPy]; rewrite QPy; Ytac0 => ->.
        rewrite ! pr1_pair ! pr2_pair; left; split => //; split => //.
       split.
         rewrite canonical_du2_pr //; aw; split; first by fprops.
         left; split =>//; case hx; first by move => [pa ].
         by rewrite Qxa; move=> [_ bad]; elim nT.
       split.
         rewrite canonical_du2_pr //; aw; split; fprops.
      right; right; split =>//.
     move=> [PPy QPy]; rewrite QPy; Ytac0 => ->.
     rewrite ! pr1_pair ! pr2_pair; right; right; split =>//.
  move=> [Qx [Qy [Px [Py aux]]]].
  have: f y=(Yo (Q (P y) = TPa) (J (J (P (P y)) TPb) TPa) (J (P (P y)) TPb)).
    by rewrite /f Y_if_not_rw //.
  have: f x = (Yo (Q (P x) = TPa)(J (J (P (P x)) TPb) TPa) (J (P (P x)) TPb)).
    by rewrite /f Y_if_not_rw //.
  case aux.
    move=> [QPx [QPy le]]; rewrite QPx QPy; Ytac0; Ytac0 => -> ->.
    rewrite !(pr1_pair, pr2_pair).
    left; split ; first (by []);split ; first (by []).
    split.
      rewrite canonical_du2_pr; aw; split; [ fprops | right ; split => // ].
      move: Px; rewrite canonical_du2_pr; move=> [pPx]; case.
       by move => [ok _].
       by rewrite QPx; move=> [_ bad]; elim nT.
    split.
      rewrite canonical_du2_pr; aw; split; [ fprops | right ; split => // ].
      move: Py; rewrite canonical_du2_pr; move=> [pPx]; case.
       by move => [ok _].
       by rewrite QPy; move=> [_ bad]; elim nT.
    right; left; split; [exact | split ; exact].
  case.
    move => [QPx [QPy le]]; Ytac0; Ytac0 => -> ->.
    right; left; aw; split; [exact | split ; exact].
  move => [QPx QPy];Ytac0; Ytac0 => -> ->.
  by right; right; aw; split.
move=> [fxF [fyF hyp]].
split; first (by exact); split; first (by exact).
move: xE; rewrite Ev canonical_du2_pr; move=> [px hx].
move: yE; rewrite Ev canonical_du2_pr; move=> [py hy].
case hx; move => [Px Qx].
   have fx: f x = (J (J (P x) TPa) TPa) by rewrite /f Y_if_rw.
   case hy; move => [Py Qy].
     have fy: f y = (J (J (P y) TPa) TPa) by rewrite /f Y_if_rw.
     left; split; first (by exact); split; first (by exact).
     case hyp.
       move=> [_ [_ [pc [pd]]]]; case.
          by rewrite fx fy; aw; move=> [_ [_ res]].
       case.
         by move=> [_ [bad _]];move: bad; rewrite fy; aw.
         by move=> [_ bad];move: bad; rewrite fy; aw.
      case. by move=> [_ [bad _]];move: bad; rewrite fy; aw.
      by move=> [_ bad];move: bad; rewrite fy; aw.
    right; right; rewrite Qx Qy; split; exact.
  right; left; split; first by ue.
have fx: f x = (Yo (Q (P x) = TPa) (J (J (P (P x)) TPb) TPa) (J (P (P x)) TPb)).
   rewrite /f Y_if_not_rw //; last by ue.
case hy; move => [Py Qy].
  have fy: f y = (J (J (P y) TPa) TPa) by rewrite /f Y_if_rw.
  elim nT; case hyp.
    move=> [Qfx [Qfy [Pfx [Pfy aux]]]].
    have aux2: Q (P (f y)) = TPa by rewrite fy; aw.
    rewrite aux2 in aux; case: aux; last first.
      by case => [ [_ [bad _]] | [_ bad]]; elim bad.
    move=> [h1 _].
    move: fx; Ytac aux3 => fx; [ move: h1 | move: Qfx ]; rewrite fx; aw.
  by rewrite fy; aw; case => [ [_ [bad _]] | [_ bad]]; elim bad.
have fy: f y = (Yo (Q (P y) = TPa) (J (J (P (P y)) TPb) TPa) (J (P (P y)) TPb)).
   rewrite /f Y_if_not_rw //; last by ue.
   split; first (by ue); split; first (by exact);split; first by exact.
move: Px; rewrite canonical_du2_pr; move=> [pPx hpx].
move: Py; rewrite canonical_du2_pr; move=> [pPy hpy].
move: fx; case hpx; move => [_ QPx]; rewrite QPx;Ytac0 => fx.
  move: fy;case hpy; move => [_ QPy]; rewrite QPy; Ytac0 => fy.
    left;eee; case hyp.
      move => [h1 [h2 [h3 [h4]]]]; rewrite fx fy !pr1_pair !pr2_pair.
      case; first by move=> [bad _]; elim nT.
      case; first by move=> [_ [_ ok]].
      by move=> [bad _]; elim nT.
    rewrite fx fy !pr1_pair !pr2_pair.
    case; first by move=> [bad _]; elim bad.
    by move=> [_ bad]; elim bad.
  right; right;split => //.
right; left;split; first by exact.
case hyp; first by move => [h1 _]; rewrite fx in h1; awi h1; contradiction.
rewrite fx !pr1_pair !pr2_pair.
move: fy; case hpy; move => [_ QPy]; rewrite QPy; Ytac0; move=> ->.
  by rewrite !pr1_pair !pr2_pair; case => [ [_ [bad _]] | [_ bad]]; elim bad.
case; first by move=> [_ [_ ok]]; split => //; move: ok; aw.
by move=> [bad _]; elim bad.
Qed.

Lemma order_prod_assoc2: forall a b c,
  order a -> order b -> order c ->
  (order_prod2 a (order_prod2 b c))
  \Is (order_prod2 (order_prod2 a b) c).
Proof.
move=> a b c oa ob oc.
have oab: order (order_prod2 a b) by apply order_prod2_order.
have obc: order (order_prod2 b c) by apply order_prod2_order.
set E:= substrate (order_prod2 a (order_prod2 b c)).
set F:= substrate (order_prod2 (order_prod2 a b) c).
set A:= substrate a; set B := substrate b; set C := substrate c.
have Ev: E = product2 (product2 C B) A.
  by rewrite /E ! order_prod2_substrate.
have Fv: F = product2 C (product2 B A).
  by rewrite /F ! order_prod2_substrate.
have CB_pr: forall z, inc z (product2 C B) =
   (fgraph z & domain z = two_points & inc (V TPa z) C & inc (V TPb z) B).
   by move=> z; rewrite product2_rw.
have BA_pr: forall z, inc z (product2 B A) =
   (fgraph z & domain z = two_points & inc (V TPa z) B & inc (V TPb z) A).
   by move=> z; rewrite product2_rw.
have Ep: forall z, inc z E =
    (fgraph z & domain z = two_points &
       (fgraph (V TPa z) & domain (V TPa z) = two_points &
        inc (V TPa (V TPa z)) C & inc (V TPb (V TPa z)) B) & inc (V TPb z) A).
   by move=> z; rewrite Ev product2_rw CB_pr; ee.
have Fp: forall z, inc z F =
    (fgraph z & domain z = two_points & inc (V TPa z) C &
       (fgraph (V TPb z) & domain (V TPb z) = two_points &
        inc (V TPa (V TPb z)) B & inc (V TPb (V TPb z)) A)).
    move=> z; rewrite Fv product2_rw BA_pr; ee.
set f := fun z => variantLc (V TPa (V TPa z))
     (variantLc (V TPb (V TPa z)) (V TPb z)).
have p1: forall x, inc x E -> inc (f x) F.
  move=> x; rewrite Ep Fp; move => [fgx [dx [[fgx1 [dx1 [xc xb]]] xa]]].
  rewrite /f; ee; bw; fprops.
have p2: forall x y, inc x E -> inc y E -> f x = f y -> x = y.
   move=> x y; rewrite Ep; move => [fgx [dx [[fgx1 [dx1 [xc xb]]] xa]]].
   rewrite /f Ep; move => [fgy [dy [[fgy1 [dy1 [yc yb]]] ya]]] sf.
   move: (f_equal (V TPa) sf) (f_equal (V TPb) sf); bw => sf1 sf2.
   move: (f_equal (V TPa) sf2) (f_equal (V TPb) sf2); bw => sf3 sf4.
   apply fgraph_exten =>//; try ue.
   rewrite dx; move=> z zdx; try_lvariant zdx.
   apply fgraph_exten =>//; try ue.
   rewrite dx1; move=> zz zdx; try_lvariant zdx.
have p3: forall y, inc y F -> exists x, (inc x E & f x = y).
  move=> y; rewrite Fp; move => [fgx [dx [xc [fgx1 [dx1 [xb xba]]]]]].
  exists (variantLc (variantLc (V TPa y) (V TPa (V TPb y)))
       (V TPb (V TPb y))).
  split; first by rewrite Ep;ee; bw; fprops.
  rewrite /f; apply fgraph_exten => //; bw; [fprops | by symmetry | ].
  move=> x xtp; try_lvariant xtp.
  apply fgraph_exten => //; bw ; [fprops | by symmetry | ].
  move=> z xtp; try_lvariant xtp.
exists (BL f E F); red;ee; aw.
  apply bl_bijective => //.
move=> x y xE yE; aw.
rewrite order_prod2_gle // /glt.
rewrite order_prod2_gle // order_prod2_gle //.
rewrite order_prod2_gle // order_prod2_substrate //.
rewrite order_prod2_substrate //.
rewrite -/A -/B -/C -Ev -Fv.
apply iff_eq.
  move: (p1 _ xE)(p1 _ yE) => xF yF.
  move=> [_ [_ aux]]; split; first (by exact) ;split; first by exact.
  move: xF; rewrite Fp; move => [fgx [dx [xc [fgx1 [dx1 [xb xba]]]]]].
  move: yF; rewrite Fp; move => [fgy [dy [yc [fgy1 [dy1 [yb yba]]]]]].
  have x1: inc (V TPb (f x)) (product2 B A) by rewrite BA_pr; ee.
  have y1: inc (V TPb (f y)) (product2 B A) by rewrite BA_pr; ee.
  set hi1:= inc (V TPb (f x)) (product2 B A).
  set hi2:= inc (V TPb (f y)) (product2 B A).
  rewrite /f; bw.
  case aux.
    move=> [Va lVb]; left; rewrite Va; ee.
  move=> [[_ [_ aux']] nse]; case aux'.
    move=> [Vaa Vba]; left.
     split; first (by exact);split; first (by exact);split; first (by exact).
     right; split =>//; dneg aux3.
     move: xE; rewrite Ep; move => [_ [_ [[tfgx1 [tdx1 _]]]] _].
     move: yE; rewrite Ep; move => [_ [_ [[tfgy1 [tdy1 _]]]] _].
     apply fgraph_exten =>//; try ue.
     rewrite tdx1; move=> z zd; try_lvariant zd.
  by move=> aux2; right.
move=> aux.
split; first (by exact);split; first by exact.
move: (xE); rewrite Ep; move => [fgx [dx [[fgx1 [dx1 [xc xb]]] xa]]].
move: (yE); rewrite Ep; move => [fgy [dy [[fgy1 [dy1 [yc yb]]] ya]]].
have x1: inc (V TPa x) (product2 C B) by rewrite CB_pr; ee.
have y1: inc (V TPa y) (product2 C B) by rewrite CB_pr; ee.
move: aux => [fxF [fyF]]; case.
  move=> [sVa [fxb [fxa]]].
  move: sVa; rewrite /f; bw => xaa.
  case.
    move=> [sVab le]; left; split =>//.
    apply fgraph_exten => //; first by ue.
    rewrite dx1; move=> z zd; try_lvariant zd.
  move=> [lt2 neq]; right; ee.
  by dneg aux; rewrite aux.
rewrite /f; bw; move=> h; right. ee.
by move: h => [_ ne]; dneg eql; rewrite eql.
Qed.

Lemma order_sum_distributive: forall x y z,
  order x -> order y -> order z ->
  (order_prod2 z (order_sum2 x y))
  \Is (order_sum2 (order_prod2 z x) (order_prod2 z y)).
Proof.
move=> a b c oa ob oc.
move: (order_sum2_order oa ob) => oz.
move: (order_prod2_order oc oa) => op2.
move: (order_prod2_order oc ob) => op1.
move: (order_prod2_order oc oz) => op3.
move:(order_sum2_order op2 op1) => op4.
set (f := fun z => J (variantLc (P (V TPa z)) (V TPb z)) (Q (V TPa z))).
set A := substrate a; set B := substrate b; set C := substrate c.
set E:= substrate (order_prod2 c (order_sum2 a b)).
set F:= substrate(order_sum2 (order_prod2 c a)(order_prod2 c b)).
have Ep: E = product2 (canonical_du2 A B) C.
   rewrite /E order_prod2_substrate // order_sum2_substrate //.
have Fp: F = canonical_du2 (product2 A C)(product2 B C).
  rewrite /F order_sum2_substrate // !order_prod2_substrate //.
have Ev: forall x, inc x E = (fgraph x & domain x = two_points &
    (is_pair (V TPa x) &
     (inc (P (V TPa x)) A & Q (V TPa x) = TPa) \/
     (inc (P (V TPa x)) B & Q (V TPa x) = TPb)) & inc (V TPb x) C).
  by move=> x; rewrite Ep product2_rw canonical_du2_pr.
have Fv: forall x, inc x F = (is_pair x & fgraph (P x) &
    domain (P x) = two_points & inc (V TPb (P x)) C &
    (inc (V TPa (P x)) A & Q x = TPa) \/
    (inc (V TPa (P x)) B & Q x = TPb)).
  move=> x; rewrite Fp canonical_du2_pr ! product2_rw.
  apply iff_eq.
     move=> [px h]; split => //; case h; move=> [[p1 [p2 [p3 p4]]] p5]; ee.
  move=> [p1 [p2 [p3 [p4 p5]]]]; split =>//.
   case p5; move=> [p6 p7]; [left | right]; ee.
have p1: forall x, inc x E -> inc (f x) F.
  move=> x; rewrite Ev; move => [fgx [dx [[px hx] qx]]].
   rewrite /f Fv !pr1_pair !pr2_pair; bw; ee.
have p2: forall x y, inc x E -> inc y E -> f x = f y -> x = y.
  move=> x y; rewrite Ev Ev /f.
  move => [fgx [dx [[px hx] qx]]] [fgy [dy [[py hy] qy]]] sf.
  move: (pr1_def sf)(pr2_def sf) => r1 r2.
  move: (f_equal (V TPa) r1)(f_equal (V TPb) r1); bw; move => [r3 r4].
  apply fgraph_exten =>//; [ ue | rewrite dx; move=> z zd; try_lvariant zd].
  apply pair_exten =>//.
have p3: forall y, inc y F -> exists x, (inc x E & f x = y).
  move=> y; rewrite Fv; move=> [py [fPy [dPy [Vby Vay]]]].
  exists (variantLc (J (V TPa (P y)) (Q y)) (V TPb (P y))).
  split.
    rewrite Ev; bw; rewrite !pr1_pair !pr2_pair; ee.
  rewrite /f; bw;aw; apply pair_exten =>//; [ fprops | aw | aw].
  apply fgraph_exten; [ fprops | exact | by bw | bw].
  move=> x xtp; try_lvariant xtp.
exists (BL f E F); red; ee; aw; first by apply bl_bijective.
move=> x y xE yE; rewrite bl_W // bl_W //.
rewrite order_sum2_gle // ! order_prod2_gle // /glt order_sum2_gle //.
have ->: Q (f x) = Q (V TPa x) by rewrite /f pr2_pair.
have ->: Q (f y) = Q (V TPa y) by rewrite /f pr2_pair.
have ->: V TPa (P (f x)) = P (V TPa x) by rewrite /f; aw; bw.
have ->: V TPa (P (f y)) = P (V TPa y) by rewrite /f; aw; bw.
have ->: V TPb (P (f x)) = V TPb x by rewrite /f; aw; bw.
have ->: V TPb (P (f y)) = V TPb y by rewrite /f; aw; bw.
have pa: is_pair (V TPa x) by move: xE; rewrite Ev; move => [_ [_[[ px _]]]].
have pb: is_pair (V TPa y) by move: yE; rewrite Ev; move => [_ [_[[ px _]]]].
apply iff_eq.
  move => [_ [_ h]].
  split; first by move: (p1 _ xE); rewrite /F order_sum2_substrate.
  split; first by move: (p1 _ yE); rewrite /F order_sum2_substrate.
  case h.
    move=> [q1 q2].
    case (equal_or_not (Q (V TPa x)) TPa)=> q3.
      left; rewrite -q1; split; first (by exact); split; first (by exact).
      split.
        rewrite product2_rw; rewrite /f pr1_pair; bw.
        move: xE; rewrite Ev; move=> [fgx [dx [[px hx] qx]]].
        case hx; move=> [q4 q5].
          by split; [fprops | split; [trivial | split]].
        by rewrite q3 in q5; elim two_points_distinct.
        split.
        rewrite product2_rw; rewrite /f pr1_pair; bw.
        move: yE; rewrite Ev; move=> [fgx [dx [[px hx] qx]]].
        rewrite q1 in q3; rewrite q3 in hx; case hx; move=> [q4 q5].
          by split; [fprops | split; [trivial | split]].
        by elim two_points_distinct.
      left; split =>//.
    right; left; split; first (by exact); split; first (by ue).
    split.
        rewrite product2_rw; rewrite /f pr1_pair; bw.
        move: xE; rewrite Ev; move=> [fgx [dx [[px hx] qx]]].
        case hx; move=> [q4 q5]; first by rewrite q5 in q3; elim q3.
        by split; [fprops | split; [trivial | split]].
    split.
        rewrite product2_rw; rewrite /f pr1_pair; bw.
        move: yE; rewrite Ev; move=> [fgx [dx [[px hx] qx]]].
        case hx; move=> [q4 q5]; first by rewrite -q1 in q5; contradiction.
        by split; [fprops | split; [trivial | split]].
    left; rewrite -q1; split =>//.
  case.
    move=> [_ [_ h1]] h2; case h1.
      move=> [q1 [q2 q3]]; left.
      split; [exact | split ; [exact | ]].
      split.
        rewrite product2_rw; rewrite /f pr1_pair; bw.
        move: xE; rewrite Ev; move=> [fgx [dx [[px hx] qx]]].
        case hx; move=> [q4 q5]; last by elim two_points_distinct; ue.
        by split; [fprops | split; [trivial | split]].
     split.
        rewrite product2_rw; rewrite /f pr1_pair; bw.
        move: yE; rewrite Ev; move=> [fgx [dx [[px hx] qx]]].
        case hx; move=> [q4 q5]; last by elim two_points_distinct; ue.
        by split; [fprops | split; [trivial | split]].
    right; split => //;dneg aux.
    apply pair_exten => //; ue.
  case.
    move => [q1 [q2 q3]]; right; left.
    split; [exact | split ; [exact | ]].
    split.
        rewrite product2_rw; rewrite /f pr1_pair; bw.
        move: xE; rewrite Ev; move=> [fgx [dx [[px hx] qx]]].
        case hx; move=> [q4 q5]; first by contradiction.
        by split; [fprops | split; [trivial | split]].
     split.
        rewrite product2_rw; rewrite /f pr1_pair; bw.
        move: yE; rewrite Ev; move=> [fgx [dx [[px hx] qx]]].
        case hx; move=> [q4 q5]; first by contradiction.
        by split; [fprops | split; [trivial | split]].
     right; split => //.
     move=> sP; elim h2; apply pair_exten => //.
     move: xE; rewrite Ev; move=> [fgx [dx [[px hx] qx]]].
     case hx; move => [_ Qx]; first (by contradiction); rewrite Qx.
     move: yE; rewrite Ev; move=> [_ [_ [[_ hy] _]]].
     case hy; move => [_ Qy]; first (by contradiction); rewrite Qy.
     done.
  move=> res; right; right; exact.
move => [_ [_ h]].
split; first by move: xE; rewrite /E order_prod2_substrate //.
split; first by move: yE; rewrite /E order_prod2_substrate //.
have pc: inc (V TPa x) (canonical_du2 (substrate a) (substrate b)).
   by move: xE; rewrite Ev canonical_du2_pr; move => [_ [_[px etc]]].
have pd: inc (V TPa y) (canonical_du2 (substrate a) (substrate b)).
   by move: yE; rewrite Ev canonical_du2_pr; move => [_ [_[px etc]]].
case h.
  move=> [q1 [q2 [q3 [q4 aux]]]]; case aux; move=> [q5 q6]; [left | right ].
    split =>//; apply pair_exten =>//; ue.
  split =>//; last by dneg neq; rewrite neq.
  split => //; split => //; left; rewrite q1 q2; split =>//; split =>//.
case.
  move=> [q1 [q2 [q3 [q4 aux]]]]; case aux; move => [q5 q6]; [left | right ].
    split =>//; apply pair_exten =>//; try ue.
    move : xE yE; rewrite ! Ev;move => [_ [_[[_ px] _]]] [_ [_[[_ qx] _]]].
    case px; move=> [_ ppx]; first by contradiction.
    case qx; move=> [_ qpx]; first (by contradiction); ue.
  split =>//; last by dneg neq; rewrite neq.
  split => //; split => //; right; ee.
move=> [q5 q6]; right; split =>//; last by dneg neq;rewrite -neq.
split => //; split => //; right;right; ee.
Qed.

Lemma order_sum_assoc3: forall a b c,
  is_ordinal a -> is_ordinal b -> is_ordinal c ->
  a +o (b +o c) = (a +o b) +o c.
Proof.
move => a b c oa ob oc.
move: (ord_sum2_ordinal ob oc) => obc.
move: (ord_sum2_ordinal oa ob) => oab.
move: (ordinal_p10 oa) => wa.
move: (ordinal_p10 ob) => wb.
move: (ordinal_p10 oc) => wc.
rewrite/ord_sum2; apply ordinal_p8.
  apply order_sum2_worder; apply ordinal_p10 => //.
  apply order_sum2_worder; apply ordinal_p10 => //.
apply orderIT with
    (order_sum2 (ordinal_o a) (order_sum2 (ordinal_o b) (ordinal_o c))).
  apply ord_sum_invariant4.
    apply orderIR; apply (worder_is_order wa).
  apply orderIS; apply ordinal_p2; apply order_sum2_worder => //.
apply orderIT with
  (order_sum2 (order_sum2 (ordinal_o a) (ordinal_o b))(ordinal_o c)).
apply order_sum_assoc2; fprops.
apply ord_sum_invariant4.
  apply ordinal_p2; apply order_sum2_worder => //.
apply orderIR; apply (worder_is_order wc).
Qed.

Lemma order_prod_assoc3: forall a b c,
  is_ordinal a -> is_ordinal b -> is_ordinal c ->
  a *o (b *o c) = (a *o b) *o c.
Proof.
move => a b c oa ob oc.
move: (ord_prod2_ordinal ob oc) => obc.
move: (ord_prod2_ordinal oa ob) => oab.
move: (ordinal_p10 oa) => wa.
move: (ordinal_p10 ob) => wb.
move: (ordinal_p10 oc) => wc.
rewrite/ord_prod2; apply ordinal_p8.
  apply order_prod2_worder; apply ordinal_p10 => //.
  apply order_prod2_worder; apply ordinal_p10 => //.
apply orderIT with
 (order_prod2 (ordinal_o a) (order_prod2 (ordinal_o b) (ordinal_o c))).
  apply ord_prod_invariant4.
    apply orderIR; apply (worder_is_order wa).
  apply orderIS; apply ordinal_p2; apply order_prod2_worder => //.
apply orderIT with
  (order_prod2 (order_prod2 (ordinal_o a) (ordinal_o b))(ordinal_o c)).
apply order_prod_assoc2; fprops.
apply ord_prod_invariant4.
  apply ordinal_p2; apply order_prod2_worder => //.
apply orderIR; apply (worder_is_order wc).
Qed.

Lemma order_sum_distributive3: forall a b c,
  is_ordinal a -> is_ordinal b -> is_ordinal c ->
  c *o (a +o b) = (c *o a) +o (c *o b).
Proof.
move => a b c oa ob oc.
move: (ord_prod2_ordinal oc oa) => obc.
move: (ord_prod2_ordinal oc ob) => ocb.
move: (ord_sum2_ordinal oa ob) => oab.
move: (ordinal_p10 oa) => wa.
move: (ordinal_p10 ob) => wb.
move: (ordinal_p10 oc) => wc.
rewrite/ord_prod2/ord_sum2; apply ordinal_p8.
  apply order_prod2_worder; apply ordinal_p10 => //.
  apply order_sum2_worder; apply ordinal_p10 => //.
apply orderIT with
  (order_prod2 (ordinal_o c) (order_sum2 (ordinal_o a) (ordinal_o b))).
  apply ord_prod_invariant4.
    apply orderIR; apply (worder_is_order wc).
  apply orderIS; apply ordinal_p2; apply order_sum2_worder => //.
apply orderIT with
     (order_sum2
        (order_prod2 (ordinal_o c) (ordinal_o a))
        (order_prod2 (ordinal_o c) (ordinal_o b))).
apply order_sum_distributive; fprops.
apply ord_sum_invariant4.
  apply ordinal_p2; apply order_prod2_worder => //.
apply ordinal_p2; apply order_prod2_worder => //.
Qed.

Lemma ord_zero_absorbing: forall r g,
   order_prod_a r g ->
    (exists i, inc i (domain g) & substrate (V i g) = emptyset)
  -> order_prod r g = emptyset.
Proof.
move=> r g la [j [jdg Vz]].
apply empty_substrate_zero; first by apply order_prod_order.
rewrite lexorder_substrate // /prod_of_substrates /fam_of_substrates.
apply is_emptyset.
move: la => [la1 [la2 [la3 la4]]].
move=> y;aw; last by gprops.
bw; move=> [fgy [dy p]]; rewrite dy in p;move: (p _ jdg); bw.
rewrite Vz=> res; elim (emptyset_pr res).
Qed.

Lemma ord0_prodr: forall x, is_ordinal x ->
  x *o 0o = 0o.
Proof.
move=> x otx; rewrite /ord_prod2 /order_prod2.
move: ordinal_0 => oz.
rewrite ord_zero_absorbing; first by apply ordinal0_pr.
apply order_prod2_axioms; apply ordinal_p11 => //.
exists TPa; bw; ee; rewrite ordinal_p7 => //.
Qed.

Lemma ord0_prodl: forall x, is_ordinal x ->
  0o *o x = 0o.
Proof.
move=> x otx; rewrite /ord_prod2 /order_prod2.
move: ordinal_0 => oz.
rewrite ord_zero_absorbing; first by apply ordinal0_pr.
apply order_prod2_axioms; apply ordinal_p11 => //.
exists TPb; bw; ee; rewrite ordinal_p7 => //.
Qed.

Lemma ord_zero_pr1: forall x, worder x -> ordinal x = 0o
  -> substrate x = emptyset.
Proof.
move=> x wor h; move: (ordinal_p2 wor); rewrite h.
move=> [f [_ [_ [bf [sf [tf _]]]]]].
rewrite (ordinal_p7 ordinal_0) in tf; rewrite sf.
have tf': target f = emptyset by ue.
apply is_emptyset; move=> y ysf.
empty_tac1 (W y f); apply inc_W_target=> //; fct_tac.
Qed.

Lemma ord_prod2_nz: forall a b, is_ordinal a -> is_ordinal b ->
   a <> 0o -> b <> 0o -> a *o b <> 0o.
Proof.
move=> a b oa ob anz bnz pz.
rewrite /ord_prod2 in pz.
have wo: worder (order_prod2 (ordinal_o a) (ordinal_o b)).
  by apply order_prod2_worder; apply ordinal_p10.
move: (ord_zero_pr1 wo pz).
rewrite (order_prod2_substrate (ordinal_p11 oa)(ordinal_p11 ob)).
rewrite (ordinal_p7 oa) (ordinal_p7 ob) => nep.
case (emptyset_dichot a);first by apply anz.
move=> [u ua].
case (emptyset_dichot b);first by apply bnz.
move=> [v vb].
empty_tac1 (variantLc v u).
rewrite product2_rw; ee; bw.
Qed.

Lemma zero_least_ordinal: forall x, is_ordinal x -> 0o <=o x.
Proof. apply ordinal_least. Qed.

Lemma zero_least_ordinal1: forall x, is_ordinal x -> x <> 0o ->
  1o <=o x.
Proof.
move=> x ox xne;rewrite ordinal_le_pr0; ee; first by apply ordinal_1.
rewrite /1o /card_one.
move=> t; aw; move=> ->.
have p1: sub emptyset x by apply emptyset_sub_any.
have p2: transitive_set emptyset by move=> u; case; case.
have p3: emptyset <> x by auto.
apply (ox _ p1 p2 p3).
Qed.

Lemma zero_least_ordinal3: forall x,
  x <=o 0o -> x = 0o.
Proof.
move=> x; rewrite /0o /card_zero ordinal_le_pr0; move=> [_[_]] h.
apply is_emptyset; move=> y yx; move: (h _ yx); case; case.
Qed.

Lemma zero_least_ordinal5: forall x,
  1o <=o x -> x <> 0o.
Proof.
move=> x h xs; rewrite xs in h; move: (zero_least_ordinal3 h) => h1.
by elim card_one_not_zero.
Qed.

Lemma ord_sum_increasing1: forall r f g ,
  order_sum_a r f -> order_sum_a r g ->
  (forall x, inc x (domain f) -> order_le (V x f) (V x g)) ->
  order_le (order_sum r f) (order_sum r g).
Proof.
move => r f g oa oa1 ale.
have o1: order (order_sum r f) by fprops.
have o2: order (order_sum r g) by fprops.
red; ee; apply (order_le_alt o1 o2).
set si:= fun j => choose (fun x => order_morphism x (V j f) (V j g)).
have sip: forall j, inc j (domain f) ->
       order_morphism (si j) (V j f) (V j g).
  move=> j jdf; move:(ale _ jdf) => [p1 [p2 [f0 [x [fx1 fx2]]]]].
  move: (order_le_alt2 p1 p2 fx1 fx2) => [res _].
  rewrite /si; apply choose_pr.
  by exists (BL (fun z => W z f0) (substrate (V j f)) (substrate (V j g))).
have dfdg: domain f = domain g by move: oa oa1 => [_ [s1 _]] [_ [s2 _]]; ue.
set E := (substrate (order_sum r g)).
set F := (disjoint_union_fam (fam_of_substrates f)).
have p1: partition_fam F (substrate (order_sum r f)).
  bw;rewrite /F /sum_of_substrates; rewrite /disjoint_union.
  apply partion_union_disjoint; rewrite /fam_of_substrates; gprops.
have p3: (domain F = domain f).
   rewrite /F/disjoint_union_fam /fam_of_substrates; bw.
set di:= fun i => (product (substrate (V i f)) (singleton i)).
set sj:= fun i => BL (fun z => J (W (P z) (si i)) i) (di i) E.
have p0: forall i, inc i (domain f) ->
   transf_axioms (fun z => J (W (P z) (si i)) i) (di i) E.
  move=> i idf z zV.
  move: (sip _ idf) => [_ [_ [[ff _] [sf [tf _]]]]].
  have aux: inc (W (P z) (si i)) (substrate (V i g)).
    rewrite tf; apply inc_W_target=>//; move: zV; rewrite /di; aw.
    move=> [_ [Ps _]]; ue.
  rewrite /E;bw; apply inc_disjoint_union1 => //; ue.
have FP: forall i, inc i (domain f) -> V i F = di i.
  move=> i idf; rewrite /F /disjoint_union_fam/fam_of_substrates ; bw.
have p2: forall i, inc i (domain F) ->
   function_prop (sj i) (V i F) E.
  rewrite p3;move=> i idf; rewrite /sj FP; red; ee; aw.
  by apply bl_function; apply p0.
move: (extension_partition p1 p2) => [[G [[fG [sG tG]] alg]] _].
rewrite p3 in alg.
have aux: forall x, inc x (source G) ->
  (inc (Q x) (domain f) & inc x (di (Q x)) & is_pair x &
     inc (P x) (substrate (V (Q x) f)) & W x G = J (W (P x) (si (Q x))) (Q x)).
  rewrite sG; bw; move => x xs; move: (du_index_pr1 xs)=> [Qxd [Pxd px]].
  have xdi: inc x (di (Q x)) by rewrite /di; aw.
  ee.
  move: (alg _ Qxd); rewrite /agrees_on; move=> [q1 [q2 q3]].
  move: (xdi); rewrite -FP // => q4.
  move: (q3 _ q4); rewrite /sj; rewrite bl_W; aw;apply p0 => //.
exists G; red; ee.
  split => //; move=> x y xs ys.
  move: (aux _ xs) (aux _ ys) => [Qx [_ [px [Px Wx]]]][Qy [_ [py [Py Wy]]]] sW.
  rewrite Wx Wy in sW; move: (pr1_def sW)(pr2_def sW) => sW1 sQ.
  apply pair_exten =>//.
  rewrite -sQ in sW1; move: (sip _ Qx) => [_ [_ [[_ injf] [sf _]]]].
  apply injf =>//; rewrite -sf //; ue.
move=> x y xs ys.
move: (aux _ xs) (aux _ ys) => [Qx [xdi [px [Px Wx]]]][Qy [ydi [py [Py Wy]]]].
rewrite (order_sum_gle oa) (order_sum_gle oa1).
have ->: (sum_of_substrates f = source G) by rewrite sG; bw.
have ->: (sum_of_substrates g = target G) by rewrite tG /E; bw.
rewrite / order_sum_r; apply iff_eq; move=>[q1 [q2 q3]].
  split;ee ; rewrite Wx Wy; aw; case q3; first by auto.
  move=> [Qxx le1]; right; ee.
  rewrite -Qxx; move: (sip _ Qx) => [_ [_ [_ [sf [tf orf]]]]].
  rewrite -orf //; rewrite -sf //; ue.
rewrite Wx Wy !pr2_pair !pr1_pair in q3.
ee;case q3; first by auto.
move=> [Qxx le1]; right; ee.
move: (sip _ Qx) => [_ [_ [_ [sf [tf orf]]]]].
rewrite -sf in orf;rewrite -Qxx in Py le1; rewrite orf //.
Qed.

Lemma ord_sum_increasing2: forall r f g, worder r ->
  fgraph f -> fgraph g -> substrate r = domain f -> substrate r = domain g ->
  (forall x, inc x (domain f) -> ordinal_le (V x f) (V x g)) ->
  (ord_sum r f) <=o (ord_sum r g).
Proof.
move=> r f g wor fgf fgg sf sg alo.
rewrite /ord_sum -order_le_compatible1.
    apply ord_sum_increasing1.
        red; bw; ee; move=> i idf; bw; move: (alo _ idf) => [aa [bb _]].
        by apply ordinal_p11.
      red; bw; ee; move=> i idf; bw; rewrite -sg sf in idf.
      by move: (alo _ idf) => [aa [bb _]];apply ordinal_p11.
    by bw; move=> x xdf; bw; [move: (alo _ xdf)=> [_ [ _ res]] |rewrite -sg sf].
  apply order_sum_worder => //; [ bw | gprops | bw ].
  move=> i idf; bw; apply ordinal_p10.
  by move: (alo _ idf) => [aa [bb _]].
apply order_sum_worder => //; [ bw | gprops | bw ].
move=> i idf; bw; apply ordinal_p10.
rewrite -sg sf in idf.
by move: (alo _ idf) => [aa [bb _]].
Qed.

Lemma ord_sum_increasing3: forall r f j,
  order_sum_a r f ->
  sub j (domain f) ->
  order_le (order_sum (induced_order r j) (restr f j)) (order_sum r f).
Proof.
move=> r f j oa jd.
move: (oa) => [or [sr [fgf alo]]].
set (g:= L (domain f) (fun z => Yo (inc z j) (V z f) emptyset)).
have dfdg: domain f = domain g by rewrite /g; bw.
have fg: fgraph g by rewrite /g; gprops.
have oa': order_sum_a r g.
  red; ee; first by ue.
  rewrite -dfdg; move=> i idg; rewrite /g; bw; Ytac1 ij; first by apply alo.
   apply emptyset_order.
have sjd': sub j (domain g) by ue.
have cz: (forall i, inc i (complement (domain g) j) -> V i g = 0o).
  by move=> i; srw;rewrite -dfdg /g; move=> [idf nij]; bw; Ytac0.
move: (zero_unit_sum_ord1 oa' sjd' cz).
have drf: domain (restr f j) = j.
  by rewrite restr_domain //; rewrite intersection2C - intersection2_sub.
have drg: domain (restr g j) = j.
  by rewrite restr_domain // -dfdg intersection2C - intersection2_sub.
have ->: (restr f j = restr g j).
    apply fgraph_exten; fprops; rewrite! drf ?drg=>//.
  move=> x xj; move: (jd _ xj) => xdf.
  by bw; rewrite /g; bw; Ytac0.
move=> oi1.
have oi2: order_isomorphic (order_sum r f) (order_sum r f).
  by apply orderIR; apply order_sum_order.
rewrite -(order_le_compatible oi1 oi2).
apply ord_sum_increasing1 => //.
rewrite -dfdg; move=> x xdg; rewrite /g; bw.
Ytac1 xj; first by apply order_le_reflexive=>//; apply alo.
move: emptyset_order => eo.
red;ee; exists (identity emptyset); exists emptyset.
have auw: sub emptyset (substrate (V x f)) by apply emptyset_sub_any.
split => //; red; ee; aw.
      apply identity_bijective.
    apply emptyset_substrate.
  apply (alo _ xdg).
move=> u v ue; elim (emptyset_pr ue).
Qed.

Lemma ord_sum_increasing4: forall r f j, worder r ->
  fgraph f -> substrate r = domain f ->
  sub j (domain f) ->
  (forall j, inc j (domain f) -> is_ordinal (V j f)) ->
  (ord_sum (induced_order r j) (restr f j)) <=o (ord_sum r f).
Proof.
move => r f j wor fgf sr sjf alo.
have sjr: sub j (substrate r) by ue.
have or: order r by move: wor => [or _].
have dr: (domain (restr f j)) = j by rewrite restr_domain1 //.
rewrite /ord_sum -order_le_compatible1; last first.
    apply order_sum_worder => //; [ bw | gprops | bw ].
    move=> i idf; bw; apply ordinal_p10; apply (alo _ idf).
  apply order_sum_worder.
        apply worder_restriction => //; ue.
      by bw; aw;rewrite dr.
    gprops.
  rewrite dr; bw; move=> i ij; bw.
  by apply ordinal_p10; apply alo; apply sjf.
set g:= (L (domain f) (fun z : Set => ordinal_o (V z f))).
have fgg: fgraph g by rewrite /g; gprops.
have sjdg: sub j (domain g) by rewrite /g; bw; ue.
have ->: (L (domain (restr f j)) (fun z : Set => ordinal_o (V z (restr f j))))
   = restr g j.
  apply fgraph_exten; [ gprops | apply (restr_fgraph _ fgg) | bw | ].
    by rewrite dr restr_domain1.
  by rewrite dr /g; bw; move => x xj; bw; apply sjf.
apply ord_sum_increasing3 => //.
by red; ee; rewrite /g; bw; move=> i idf; bw; apply ordinal_p11; apply alo.
Qed.

Lemma ord_prod_increasing1: forall r f g ,
  order_prod_a r f -> order_prod_a r g ->
  (forall x, inc x (domain f) -> order_le (V x f) (V x g)) ->
  order_le (order_prod r f) (order_prod r g).
Proof.
move => r f g oa oa1 ale.
have o1: order (order_prod r f) by fprops.
have o2: order (order_prod r g) by fprops.
red; ee; apply (order_le_alt o1 o2).
set si:= fun j => choose (fun x => order_morphism x (V j f) (V j g)).
have sip: forall j, inc j (domain f) ->
       order_morphism (si j) (V j f) (V j g).
  move=> j jdf; move:(ale _ jdf) => [p1 [p2 [f0 [x [fx1 fx2]]]]].
  move: (order_le_alt2 p1 p2 fx1 fx2) => [res _].
  rewrite /si; apply choose_pr.
  by exists (BL (fun z => W z f0) (substrate (V j f)) (substrate (V j g))).
have dfdg: domain f = domain g by move: oa oa1 => [_ [s1 _]] [_ [s2 _]]; ue.
set E:= substrate (lexicographic_order r f).
set F := substrate(lexicographic_order r g).
have Ep: E = (prod_of_substrates f) by apply lexorder_substrate => //.
have Fp: F = (prod_of_substrates g) by apply lexorder_substrate => //.
have Ep1: forall z, inc z E =
    (fgraph z & domain z = domain f &
       (forall i, inc i (domain f) -> inc (V i z) (substrate (V i f)))).
  move: oa => [wor [sr [fgf alof]]].
  move=> z; rewrite Ep;aw; red; ee.
have Fp1: forall z, inc z F =
    (fgraph z & domain z = domain f &
       (forall i, inc i (domain f) -> inc (V i z) (substrate (V i g)))).
  move: oa1 => [wor [sr [fgf alof]]].
  move=> z; rewrite Fp dfdg;aw; red; ee.
set h:= fun z => L (domain f) (fun i => W (V i z) (si i)).
have ta: transf_axioms h E F.
  move => t; rewrite Ep1 Fp1; move=> [fgt [dt als]].
  rewrite /h; bw;ee; move=> i idf; bw.
  move: (sip _ idf) => [_ [_ [[fsi _] [ssi [tsi _]]]]].
  by rewrite tsi; apply inc_W_target =>//; rewrite -ssi;apply als.
have hi: forall u v, inc u E -> inc v E -> h u = h v -> u = v.
  move=> u v; rewrite Ep1 Ep1 /h; move=> [fgu [du alu]][fgv [dv alv]] sh.
  apply fgraph_exten =>//; first by ue.
  rewrite du; move=> x xdf; move: (f_equal (V x) sh); bw => sW.
  move: (sip _ xdf) => [_ [_ [[fsi insi] [ssi [tsi _]]]]].
  apply insi =>//; rewrite -ssi; [ apply alu => // |apply alv => //].
exists (BL h E F); red; aw; ee.
  apply bl_injective => //.
move=> x y xE yE; rewrite /h bl_W // bl_W //.
rewrite order_prod_gle // order_prod_gle //.
rewrite -Ep -Fp.
have srdf: substrate r = domain f by move: oa => [_ [sr _]].
move: (xE)(yE); rewrite Ep1 Ep1; move=> [fgx [dx alx]][fgy [dy aly]].
apply iff_eq.
  move => [_ [_ aux]]; split; first (by apply ta); split; first by apply ta.
  case aux; first by move=>->; left.
  move=> [j [jsr [jlt jle]]]; right; ex_tac; split.
    rewrite srdf in jsr;move: jlt; bw.
    move: (sip _ jsr) => [_ [_ [[fsi insi] [ssi [tsi jsrp]]]]].
    have vsx: inc (V j x) (source (si j)) by rewrite -ssi; apply alx => //.
    have vsy: inc (V j y) (source (si j)) by rewrite -ssi; apply aly => //.
    rewrite /glt -jsrp //;move=> [p1 p2]; split =>//; dneg sV; apply insi=>//.
  move=> i ij.
  have idf: inc i (domain f) by rewrite -srdf; order_tac.
  by move: (jle _ ij); bw; move=> ->.
move=> [h1 [h2 aux]].
split=> //; split => //; case aux.
  move => sh; left; apply hi =>//.
move=> [j [jsr [jlt jle]]]; right; ex_tac; split.
rewrite srdf in jsr;move: jlt; bw.
move: (sip _ jsr) => [_ [_ [[fsi insi] [ssi [tsi jsrp]]]]].
rewrite /glt -jsrp; first by move=> [p1 p2]; split =>//; dneg sV; ue.
    rewrite -ssi; apply alx => //.
  rewrite -ssi; apply aly => //.
move=> i ij.
have idf: inc i (domain f) by rewrite -srdf; order_tac.
move: (jle _ ij); bw.
move: (sip _ idf) => [_ [_ [[fsi insi] [ssi [tsi jsrp]]]]].
apply insi; rewrite - ssi;[ apply alx => // | apply aly => //].
Qed.

Lemma ord_prod_increasing2: forall r f g, worder r ->
  fgraph f -> fgraph g -> substrate r = domain f -> substrate r = domain g ->
  (forall x, inc x (domain f) -> ordinal_le (V x f) (V x g)) ->
  finite_set (substrate r) ->
  (ord_prod r f) <=o (ord_prod r g).
Proof.
move=> r f g wor fgf fgg sf sg alo fsr.
rewrite /ord_prod-order_le_compatible1.
    apply ord_prod_increasing1.
        red; red; bw; ee; move=> i idf; bw; move: (alo _ idf) => [aa [bb _]].
        by apply ordinal_p11.
      red;red; bw; ee; move=> i idf; bw; rewrite -sg sf in idf.
      by move: (alo _ idf) => [aa [bb _]];apply ordinal_p11.
    by bw; move=> x xdf; bw; [move: (alo _ xdf)=> [_ [ _ res]] |rewrite -sg sf].
  apply order_prod_worder => //; [ bw | gprops | bw ].
  move=> i idf; bw; apply ordinal_p10.
  by move: (alo _ idf) => [aa [bb _]].
apply order_prod_worder => //; [ bw | gprops | bw ].
move=> i idf; bw; apply ordinal_p10.
rewrite -sg sf in idf.
by move: (alo _ idf) => [aa [bb _]].
Qed.

Lemma ord_prod_increasing3: forall r f j,
  order_prod_a r f ->
  sub j (domain f) ->
  (forall x, inc x (complement (domain f) j) ->
     substrate (V x f) <> emptyset) ->
  order_le (order_prod (induced_order r j) (restr f j)) (order_prod r f).
Proof.
move=> r f j oa jd anot.
move: (oa) => [or [sr [fgf alo]]].
set (g:= L (domain f) (fun z => Yo (inc z j) (V z f) (ordinal_o 1o))).
have dfdg: domain f = domain g by rewrite /g; bw.
have fg: fgraph g by rewrite /g; gprops.
have oa': order_prod_a r g.
  red; red;ee; first by ue.
  rewrite -dfdg; move=> i idg; rewrite /g; bw; Ytac1 ij; first by apply alo.
  by apply (ordinal_pr51 ordinal_1).
have sjd': sub j (domain g) by ue.
have cz: (forall i, inc i (complement (domain g) j) ->
  is_singleton (substrate (V i g))).
  move=> i; srw;rewrite -dfdg /g; move=> [idf nij]; bw; Ytac0.
  by rewrite (ordinal_p7 ordinal_1); exists emptyset.
move: (one_unit_prod_ord1 oa' sjd' cz).
have drf: domain (restr f j) = j.
  by rewrite restr_domain //; rewrite intersection2C - intersection2_sub.
have drg: domain (restr g j) = j.
  by rewrite restr_domain // -dfdg intersection2C - intersection2_sub.
have ->: (restr f j = restr g j).
    apply fgraph_exten; fprops; rewrite! drf ?drg=>//.
  move=> x xj; move: (jd _ xj) => xdf.
  by bw; rewrite /g; bw; Ytac0.
move=> oi1.
have oi2: order_isomorphic (order_prod r f) (order_prod r f).
  by apply orderIR; apply order_prod_order.
rewrite -(order_le_compatible oi1 oi2).
apply ord_prod_increasing1 => //.
rewrite -dfdg; move=> x xdg; rewrite /g; bw.
Ytac1 nij; first by apply order_le_reflexive=>//; apply alo.
have p2: order (V x f) by move: oa => [q1 [q2 [q3 q4]]]; apply q4.
have [y yd]: nonempty (substrate (V x f)).
  case (emptyset_dichot (substrate (V x f))) => // p1.
  have xcz: inc x (complement (domain f) j) by srw; ee.
  move: (anot _ xcz) => p4; contradiction.
have s1: sub (singleton y) (substrate (V x f)) by apply sub_singleton.
set h:= BL (fun z => y) (singleton emptyset) (singleton y).
have ta: transf_axioms (fun _ : Set => y) (singleton emptyset) (singleton y).
  by move=> t; aw.
red;ee; exists h; exists (singleton y); ee.
rewrite /h;red; ee; bw; aw.
  apply bl_bijective => //; first by move=> u v; aw; move => -> ->.
  move=> u; aw; move => ->; exists emptyset; aw; ee.
move => u v; rewrite ! singleton_rw; move=> -> ->; aw.
apply iff_eq => un; first by order_tac.
rewrite /1o/card_one; ee.
Qed.

Lemma ord_prod_increasing4: forall r f j, worder r ->
  fgraph f -> substrate r = domain f ->
  sub j (domain f) ->
  (forall j, inc j (domain f) -> is_ordinal (V j f)) ->
  finite_set (substrate r) ->
  (forall x, inc x (complement (domain f) j) -> V x f <> 0o) ->
  (ord_prod (induced_order r j) (restr f j)) <=o (ord_prod r f).
Proof.
move => r f j wor fgf sr sjf alo fsr alnz.
have sjr: sub j (substrate r) by ue.
have or: order r by move: wor => [or _].
have dr: (domain (restr f j)) = j by rewrite restr_domain1 //.
rewrite /ord_prod -order_le_compatible1; last first.
      apply order_prod_worder => //; [ bw | gprops | bw ].
      move=> i idf; bw; apply ordinal_p10; apply (alo _ idf).
    apply order_prod_worder.
          apply worder_restriction => //; ue.
        by bw; aw;rewrite dr.
      gprops.
    rewrite dr; bw; move=> i ij; bw.
    by apply ordinal_p10; apply alo; apply sjf.
  aw;apply (sub_finite_set sjr fsr).
set g:= (L (domain f) (fun z : Set => ordinal_o (V z f))).
have fgg: fgraph g by rewrite /g; gprops.
have sjdg: sub j (domain g) by rewrite /g; bw; ue.
have ->: (L (domain (restr f j)) (fun z : Set => ordinal_o (V z (restr f j))))
   = restr g j.
  apply fgraph_exten; [ gprops | apply (restr_fgraph _ fgg) | bw | ].
    by rewrite dr restr_domain1.
  by rewrite dr /g; bw; move => x xj; bw; apply sjf.
apply ord_prod_increasing3 => //.
  by red;red; ee; rewrite /g; bw; move=> i idf; bw; apply ordinal_p11;apply alo.
rewrite /g; bw;move=> x xc; move: (alnz _ xc).
have xdf: inc x (domain f) by move: xc; srw; intuition.
by bw; rewrite (ordinal_p7 (alo _ xdf)).
Qed.

Lemma ord_sum2_increasing1: forall a b c d,
  ordinal_le a b -> ordinal_le c d ->
  ordinal_le (ord_sum2 a c) (ord_sum2 b d).
Proof.
move=> a b c d le1 le2.
rewrite !ord_sum2_pr; apply ord_sum_increasing2; fprops.
- apply worder_canonical_doubleton_order.
- rewrite substrate_canonical_doubleton_order; bw.
- rewrite substrate_canonical_doubleton_order; bw.
bw; move=> x xtp; try_lvariant xtp.
Qed.

Lemma ord_sum2_increasing2: forall a b,
  is_ordinal a -> is_ordinal b -> a <=o (a +o b).
Proof.
move=> a b oa ob.
set j := singleton TPa; set g := (variantLc a b).
rewrite ord_sum2_pr.
move: worder_canonical_doubleton_order => p1.
have p2: fgraph g by rewrite /g;fprops.
have p3: substrate canonical_doubleton_order = domain g.
  by rewrite /g substrate_canonical_doubleton_order; bw.
have p4: sub j (domain g).
  move=> t; rewrite /j /g; bw; aw; move=> ->; fprops.
have p5: (forall i, inc i (domain g) -> is_ordinal (V i g)).
  rewrite /g;bw; move=> i jd; try_lvariant jd.
move:(ord_sum_increasing4 p1 p2 p3 p4 p5).
have res: V TPa (restr g j) = a by rewrite /g/j; bw; fprops.
have q1: sub j (substrate canonical_doubleton_order) by ue.
have q2: substrate (induced_order canonical_doubleton_order j) = j.
  by aw; move: p1 => [or _ ].
have q3: worder (induced_order canonical_doubleton_order j)
  by apply worder_restriction.
have q4: domain (restr g j) = j by rewrite /g restr_domain1 //.
have q5: inc TPb (singleton TPb) by fprops.
rewrite (ord_sum_singleton (e:= TPa)) => //; try ue; bw; fprops.
Qed.

Lemma ord_sum2_increasing3: forall a b,
  is_ordinal a -> is_ordinal b -> b <=o (a +o b).
Proof.
move=> a b oa ob.
set j := singleton TPb; set g := (variantLc a b).
rewrite ord_sum2_pr.
move: worder_canonical_doubleton_order => p1.
have p2: fgraph g by rewrite /g;fprops.
have p3: substrate canonical_doubleton_order = domain g.
  by rewrite /g substrate_canonical_doubleton_order; bw.
have p4: sub j (domain g).
  move=> t; rewrite /j /g; bw; aw; move=> ->; fprops.
have p5: (forall i, inc i (domain g) -> is_ordinal (V i g)).
  rewrite /g;bw; move=> i jd; try_lvariant jd.
move:(ord_sum_increasing4 p1 p2 p3 p4 p5).
have res: V TPb (restr g j) = b by rewrite /g/j; bw; fprops.
have q1: sub j (substrate canonical_doubleton_order) by ue.
have q2: substrate (induced_order canonical_doubleton_order j) = j.
  by aw; move: p1 => [or _ ].
have q3: worder (induced_order canonical_doubleton_order j)
  by apply worder_restriction.
have q4: domain (restr g j) = j by rewrite /g restr_domain1 //.
have q5: inc TPa (singleton TPa) by fprops.
rewrite (ord_sum_singleton (e:= TPb)) => //; try ue; bw; fprops.
Qed.

Lemma ord_prod2_increasing1: forall a b c d,
  a <=o b -> c <=o d -> (a *o c) <=o (b *o d).
Proof.
move=> a b c d le1 le2; rewrite !ord_prod2_pr.
apply ord_prod_increasing2; fprops.
- apply worder_canonical_doubleton_order.
- rewrite substrate_canonical_doubleton_order; bw.
- rewrite substrate_canonical_doubleton_order; bw.
- bw; move=> x xtp; try_lvariant xtp.
- rewrite substrate_canonical_doubleton_order -two_points_pr2.
  apply doubleton_finite.
Qed.

Lemma ord_prod2_increasing2: forall a b,
  is_ordinal a -> is_ordinal b -> b <> 0o -> a <=o (a *o b).
Proof.
move=> a b oa ob bnz.
set j := singleton TPb.
rewrite ord_prod2_pr.
move: worder_canonical_doubleton_order => p1.
have p2: fgraph (variantLc b a) by fprops.
have p3: substrate canonical_doubleton_order = domain (variantLc b a).
  by rewrite substrate_canonical_doubleton_order; bw.
have p4: sub j (domain (variantLc b a)).
  move=> t; rewrite /j; bw; aw; move=> ->; fprops.
have p5: (forall i, inc i (domain (variantLc b a)) ->
   is_ordinal (V i (variantLc b a))).
  bw; move=> i jd; try_lvariant jd.
have p6: finite_set (substrate canonical_doubleton_order).
  rewrite substrate_canonical_doubleton_order -two_points_pr2.
  apply doubleton_finite.
have p7: (forall x, inc x (complement (domain (variantLc b a)) j)
     -> V x (variantLc b a) <> 0o).
  rewrite /j;move=> x; bw; srw; aw; move=> [xtp]; move:xtp=> [] ->; bw.
  by move => h; elim h.
move:(ord_prod_increasing4 p1 p2 p3 p4 p5 p6 p7).
have res: V TPb (restr (variantLc b a) j) = a by bw; rewrite /j; fprops.
have q1: sub j (substrate canonical_doubleton_order) by ue.
have q2: substrate (induced_order canonical_doubleton_order j) = j.
  by aw; move: p1 => [or _ ].
have q3: worder (induced_order canonical_doubleton_order j)
  by apply worder_restriction.
have q4: domain (restr (variantLc b a) j) = j by rewrite restr_domain1 //.
have q5: inc TPb (singleton TPb) by fprops.
rewrite (ord_prod_singleton (e:= TPb)) => //; try ue; bw; fprops.
Qed.

Lemma ord_prod2_increasing3: forall a b,
  is_ordinal a -> is_ordinal b -> a <> 0o -> b <=o (a *o b).
Proof.
move=> a b oa ob bnz.
set j := singleton TPa.
rewrite ord_prod2_pr.
move: worder_canonical_doubleton_order => p1.
have p2: fgraph (variantLc b a) by fprops.
have p3: substrate canonical_doubleton_order = domain (variantLc b a).
  by rewrite substrate_canonical_doubleton_order; bw.
have p4: sub j (domain (variantLc b a)).
  move=> t; rewrite /j; bw; aw; move=> ->; fprops.
have p5: (forall i, inc i (domain (variantLc b a)) ->
   is_ordinal (V i (variantLc b a))).
  bw; move=> i jd; try_lvariant jd.
have p6: finite_set (substrate canonical_doubleton_order).
  rewrite substrate_canonical_doubleton_order -two_points_pr2.
  apply doubleton_finite.
have p7: (forall x, inc x (complement (domain (variantLc b a)) j)
     -> V x (variantLc b a) <> 0o).
  rewrite /j;move=> x; bw; srw; aw; move=> [xtp]; move:xtp=> [] ->; bw.
  by move => h; elim h.
move:(ord_prod_increasing4 p1 p2 p3 p4 p5 p6 p7).
have res: V TPa (restr (variantLc b a) j) = b by bw; rewrite /j; fprops.
have q1: sub j (substrate canonical_doubleton_order) by ue.
have q2: substrate (induced_order canonical_doubleton_order j) = j.
  by aw; move: p1 => [or _ ].
have q3: worder (induced_order canonical_doubleton_order j)
  by apply worder_restriction.
have q4: domain (restr (variantLc b a) j) = j by rewrite restr_domain1 //.
have q5: inc TPa (singleton TPa) by fprops.
rewrite (ord_prod_singleton (e:= TPa)) => //; try ue; bw; fprops.
Qed.

Lemma ord_sum2_increasing4: forall a b c,
  a <=o b -> is_ordinal c -> (a +o c) <=o (b +o c).
Proof. move=> a b c leab oc; apply ord_sum2_increasing1 => //; ord_tac. Qed.

Lemma ord_sum2_increasing5: forall a b c,
  a <=o b -> is_ordinal c -> (c +o a) <=o (c +o b).
Proof. move=> a b c leab oc; apply ord_sum2_increasing1 => //; ord_tac. Qed.

Lemma ord_prod2_increasing4: forall a b c,
  a <=o b -> is_ordinal c -> (a *o c) <=o (b *o c).
Proof. move=> a b c leab oc; apply ord_prod2_increasing1 => //; ord_tac. Qed.

Lemma ord_prod2_increasing5: forall a b c,
  a <=o b -> is_ordinal c -> (c *o a) <=o (c *o b).
Proof. move=> a b c leab oc; apply ord_prod2_increasing1 => //; ord_tac. Qed.

Lemma cardinal_of_ordinal: forall x, worder x ->
  cardinal (ordinal x) = cardinal (substrate x).
Proof.
move=> x wx; move: (ordinal_p2 wx); move=> [f [_ [_ [bf [sf [tf _]]]]]].
symmetry.
rewrite (ordinal_p7 (ordinal_p1 wx)) in tf.
rewrite sf tf; aw; exists f; ee.
Qed.

Lemma cardinal_ord_sum: forall a b, is_ordinal a -> is_ordinal b ->
  cardinal (a +o b) = (cardinal a) +c (cardinal b).
Proof.
move=> a b oa ob.
move: (ordinal_p11 oa)(ordinal_p11 ob)=> p1 p2.
set h := (order_sum2 (ordinal_o a) (ordinal_o b)).
have wh:worder h by rewrite /h; apply order_sum2_worder;apply ordinal_p10.
rewrite (cardinal_of_ordinal wh) order_sum2_substrate //.
rewrite (ordinal_p7 oa) (ordinal_p7 ob).
have ->: cardinal (canonical_du2 a b) = card_plus a b by [].
by symmetry;apply card_plus_pr2; apply double_cardinal.
Qed.

Lemma cardinal_ord_sum2: forall a b,
  inc a Bnat -> inc b Bnat ->
  a +o b = a +c b.
Proof.
move=> a b aB bB.
have ba: is_ordinal a by apply cardinal_ordinal; fprops.
have bb: is_ordinal b by apply cardinal_ordinal; fprops.
move: (cardinal_ord_sum ba bb).
set X := ord_sum2 a b.
have boX: is_ordinal X by rewrite /X; apply ord_sum2_ordinal.
rewrite (Bnat_cardinal aB) (Bnat_cardinal bB) => h.
have fs: finite_set X by red;rewrite -inc_Bnat h; hprops.
rewrite -(cardinal_of_cardinal (finite_cardinal_pr1 (ordinal_finite1 boX fs))).
done.
Qed.

Lemma ord_11_2: 1o +o 1o = 2o.
Proof.
by move:(cardinal_ord_sum2 inc1_Bnat inc1_Bnat); rewrite card_two_pr.
Qed.

Lemma ord_plus_int_omega: forall n, inc n Bnat ->
  ord_sum2 n ord_omega = ord_omega.
Proof.
move=> n nB.
move: Bnat_order_worder => woB.
move: (interval_Bnatco_worder nB) => woi.
rewrite ord_omega_pr (finite_ordinal1 nB).
apply ord_sum_invariant5 =>//.
have os: order (order_sum2 (interval_Bnatco n) Bnat_order) by fprops.
have oB: order Bnat_order by fprops.
have oi: order (interval_Bnatco n) by fprops.
set E:= substrate (order_sum2 (interval_Bnatco n) Bnat_order).
set F:= substrate Bnat_order.
have Fp: F = Bnat by rewrite /F Bnat_order_substrate.
have Ep: E = canonical_du2 (interval_co_0a n) F.
   aw; rewrite /E order_sum2_substrate // interval_Bnatco_substrate //.
set f:= fun z => Yo (Q z = TPa) (P z) (card_plus n (P z)).
have tpd: TPb <> TPa by fprops.
have ta: transf_axioms f E F.
  move=> t; rewrite Ep Fp canonical_du2_pr //; move=> [pt h].
  rewrite /f; case h; move => [Pt Qt]; rewrite Qt; Ytac0; last by hprops.
     apply (sub_interval_co_0a_Bnat Pt).
have fi: forall x y, inc x E -> inc y E -> f x = f y -> x = y.
  move=> x y; rewrite Ep canonical_du2_pr // canonical_du2_pr // /f.
  move=> [px hx][py hy].
  case hx; case hy; move=> [Py Qy][Px Qx]; rewrite Qx Qy; Ytac0; Ytac0 => aux.
        apply pair_exten => //; ue.
      move: Px; rewrite interval_co_0a_pr2//; move=> pxn.
      have npx: cardinal_le n (P x).
        rewrite aux; apply sum_increasing3 =>//; [ | rewrite Fp in Py]; fprops.
      co_tac.
    move: Py; rewrite interval_co_0a_pr2//; move=> pxn.
    have npx: cardinal_le n (P y).
      rewrite -aux; apply sum_increasing3 =>//; [ | rewrite Fp in Px]; fprops.
    co_tac.
  apply pair_exten => //; last by ue.
  rewrite Fp in Px Py;apply plus_simplifiable_left with n =>//.
exists (BL f E F); red; ee; aw.
  apply bl_bijective => //.
  rewrite Fp; move=> y yF.
  have cn: is_cardinal n by fprops.
  have cy: is_cardinal y by fprops.
  case (cardinal_le_total_order2 cn cy) => cny.
    exists (J (card_sub y n) TPb); rewrite Ep canonical_du2_pr //; ee.
      right; aw; rewrite Fp; ee.
    rewrite /f; aw; rewrite Y_if_not_rw //; apply (card_sub_pr cny).
  exists (J y TPa); rewrite Ep canonical_du2_pr //; ee.
    left; aw;rewrite interval_co_0a_pr2//.
  by rewrite /f; aw;rewrite Y_if_rw.
move=> x y xE yE.
rewrite order_sum2_gle // ! Bnat_order_le /Bnat_le.
rewrite bl_W // bl_W//.
rewrite Fp in ta.
apply iff_eq; move=> [p1 [p2 p3]].
  split; first (by apply ta); split; first by apply ta.
  rewrite /f; case p3.
    move=> [q1 [q2 q3]]; rewrite q1 q2; Ytac0; Ytac0.
    rewrite interval_Bnatco_related // in q3; intuition.
  case.
    move=> [q1 [q2 [q3 [q4 q5]]]]; Ytac0; Ytac0.
    apply sum_increasing2 => //; fprops.
  move=> [q1 q2]; Ytac0; Ytac0.
  move: xE; rewrite Ep canonical_du2_pr //; move => [px]; case.
    rewrite interval_co_0a_pr2//.
    move=> [[ple _] _].
    have le2: cardinal_le n (card_plus n (P y)).
       apply sum_increasing3 => //; fprops.
       move: yE; rewrite Ep canonical_du2_pr //; move => [qx]; case.
       move=> [py _]; move: (sub_interval_co_0a_Bnat py) => pB; fprops.
       rewrite Fp; move=> [py _]; fprops.
       co_tac.
  by move=> [_ Qx]; elim tpd;rewrite -q1 -Qx.
split; first by move: xE; rewrite /E order_sum2_substrate //.
split; first by move: yE; rewrite /E order_sum2_substrate //.
move: xE yE; rewrite Ep canonical_du2_pr // canonical_du2_pr // /f.
move=> [px hx][py hy].
move: p3; rewrite /f.
case hx; case hy; move=> [Py Qy][Px Qx]; rewrite Qx Qy; Ytac0; Ytac0 => fp.
      left; ee; rewrite interval_Bnatco_related //; ee.
      move: Py; rewrite interval_co_0a_pr2//.
    right; right; ee.
  have le2: cardinal_le n (card_plus n (P x)).
    apply sum_increasing3 => //; fprops; rewrite Fp in Px; fprops.
  have le3: cardinal_le n (P y) by co_tac.
  move: Py; rewrite interval_co_0a_pr2// => le4; co_tac.
right; left; ee; try ue.
apply plus_le_simplifiable with n => //; ue.
Qed.

Lemma ord_mult_int_omega: forall n, inc n Bnat ->
  n <> 0c -> n *o \omega = \omega.
Proof.
move=> n nB nz.
move: Bnat_order_worder => woB.
move: (interval_Bnatco_worder nB) => woi.
rewrite ord_omega_pr (finite_ordinal1 nB).
apply ord_prod_invariant5 =>//.
have os: order (order_prod2 (interval_Bnatco n) Bnat_order) by fprops.
have oB: order Bnat_order by fprops.
have oi: order (interval_Bnatco n) by fprops.
set E:= substrate (order_prod2 (interval_Bnatco n) Bnat_order).
set F:= substrate Bnat_order.
have Fp: F = Bnat by rewrite /F Bnat_order_substrate.
have Ep: E = product2 F (interval_co_0a n).
   aw; rewrite /E order_prod2_substrate // interval_Bnatco_substrate //.
have ep1: forall x, inc x E ->
  (inc (V TPa x) Bnat & inc (V TPb x) Bnat & cardinal_lt (V TPb x) n).
  move=> x; rewrite Ep Fp product2_rw; move=> [fgx [dx [xa xb]]].
  split => //; split.
    apply (sub_interval_co_0a_Bnat xb).
  move: xb;rewrite interval_co_0a_pr2//.
set f:= fun z => card_plus (card_mult n (V TPa z)) (V TPb z).
have ta: forall x, inc x E -> inc (f x) F.
  move=> x xE; rewrite Fp /f; move: (ep1 _ xE) => [p1 [p2 _]]; hprops.
have fi: forall x y, inc x E -> inc y E -> f x = f y -> x = y.
  rewrite /f=> x y xE yE sf.
  move: (ep1 _ xE) (ep1 _ yE)=> [p1 [p2 p3]][p4 [p5 p6]].
  set m := card_plus (card_mult n (V TPa x)) (V TPb x).
  have mB: inc m Bnat by rewrite /m; hprops.
  have d1: (division_prop m n (V TPa x) (V TPb x)) by red;ee.
  have d2: (division_prop m n (V TPa y) (V TPb y)) by red;ee.
  move: (division_unique mB nB p1 p2 p4 p5 nz d1 d2) => [sa sb].
  move: xE yE; rewrite Ep product2_rw product2_rw.
  move => [fgx [dx _]][fgy [dy _]].
  apply fgraph_exten=>//; first by ue.
  rewrite dx; move=> i idx; try_lvariant idx.
exists (BL f E F); red; aw;ee.
  apply bl_bijective => //; rewrite Ep Fp.
  move=> y yB; move: (Bnat_division yB nB nz) => [rB [qB [d1 d2]]].
  exists (variantLc (card_quo y n) (card_rem y n)); split.
    rewrite product2_rw; bw; ee.
      rewrite -inc_Bnat//.
    rewrite interval_co_0a_pr2//.
  by rewrite /f; bw.
move=> x y xE yE; rewrite bl_W // bl_W//.
rewrite Bnat_order_le /Bnat_le.
rewrite order_prod2_gle //.
apply iff_eq; move=> [p1 [p2 aux]].
  rewrite -Fp;split; first (by apply ta);split; first by apply ta.
  rewrite /f; case aux.
    move=> [vv]; rewrite vv.
    rewrite interval_Bnatco_related //; move=> [lea _].
    apply sum_increasing2; fprops.
  rewrite /glt; rewrite Bnat_order_le /Bnat_le; move=> [[axB [ayB axy]] naxy].
  set pr1 := (card_mult n (V TPa x)).
  set pr2 := (card_mult n (V TPa y)).
  have p1B: inc pr1 Bnat by rewrite /pr1;hprops.
  have p2B: inc pr2 Bnat by rewrite /pr2;hprops.
  move: (ep1 _ xE) (ep1 _ yE)=> [r1 [r2 [r3 _]]][r4 [r5 r6]].
  have le1: cardinal_le pr2 (card_plus pr2 (V TPb y)).
    apply sum_increasing3; fprops.
  have le2: cardinal_le (card_plus pr1 (V TPb x)) (card_plus pr1 n).
    apply sum_increasing2; fprops.
  have le3: (card_plus pr1 n = card_mult n (succ (V TPa x))).
    rewrite /pr1 (Bsucc_rw r1) cardinal_distrib_prod_sum3 one_unit_prodr;fprops.
  rewrite le3 in le2.
  have le4: cardinal_le (succ (V TPa x)) (V TPa y).
    rewrite lt_n_succ_le0 //; fprops.
  have le5: cardinal_le (card_mult n (succ (V TPa x)))(card_mult n (V TPa y)).
     apply product_increasing2; fprops.
  have le6: cardinal_le (card_plus pr1 (V TPb x)) (card_mult n (V TPa y)).
    co_tac.
  co_tac.
split; first by move: xE; rewrite /E order_prod2_substrate.
split; first by move: yE; rewrite /E order_prod2_substrate.
move: aux; rewrite /f => fv.
set ax:= V TPa x; set bx:= V TPb x; set ay:= V TPa y; set byv:= V TPb y.
move: (ep1 _ xE) (ep1 _ yE)=> [r1 [r2 [r3 _]]][r4 [r5 r6]].
have cax: is_cardinal ax by fprops.
have cay: is_cardinal ay by fprops.
case (cardinal_le_total_order1 cax cay).
  move=> sa; left; ee.
  rewrite interval_Bnatco_related //; ee.
  rewrite /bx /byv.
  move:fv. rewrite -/ax -/ay sa.
  apply plus_le_simplifiable; hprops.
case; first by move=> [r7 rr8]; right;rewrite /glt Bnat_order_le /Bnat_le; ee.
have le1: cardinal_le (card_mult n (V TPa x))
    (card_plus (card_mult n (V TPa x)) (V TPb x)).
  apply sum_increasing3; fprops.
have le2: cardinal_le (card_mult n (V TPa x))
    (card_plus (card_mult n (V TPa y)) (V TPb y)) by co_tac.
move: (r6) => [r6a r6b].
have : cardinal_lt (card_plus (card_mult n (V TPa y)) (V TPb y))
 (card_plus (card_mult n (V TPa y)) n).
  split.
     apply sum_increasing2; [apply cardinal_le_reflexive; fprops | exact ].
  dneg aux.
  apply plus_simplifiable_left with (card_mult n (V TPa y)); hprops.
have ->: (card_plus (card_mult n (V TPa y)) n = card_mult n (succ (V TPa y))).
  rewrite (Bsucc_rw r4) cardinal_distrib_prod_sum3 one_unit_prodr;fprops.
move => le4.
have le5: cardinal_lt (card_mult n (V TPa x)) (card_mult n (succ (V TPa y))).
  co_tac.
rewrite -lt_n_succ_le0 // => le6.
have le7: cardinal_le (card_mult n (succ ay))(card_mult n ax).
  apply product_increasing2; fprops.
rewrite -/ay in le5.
co_tac.
Qed.

Lemma ordinal_a_ne_ab: forall a b, is_ordinal a -> is_ordinal b ->
   a = a +o b -> b = 0o.
Proof.
move=> a b oa ob; rewrite /ord_sum2.
set c:= order_sum2 _ _ .
move: (ordinal_p11 oa) => ora.
move: (ordinal_p11 ob) => orb.
move=> aoc.
set A := Zo (substrate c) (fun z => (Q z) = TPa).
have p1: worder c by rewrite /c; apply order_sum2_worder;apply ordinal_p10.
have orc: order c by move: p1 => [oc _].
have p2: sub A (substrate c) by rewrite /A; apply Z_sub.
have p3: is_segment c A.
   red; ee; move=> x y; rewrite /A ! Z_rw ; move => [_ Qx] cx;split.
     order_tac.
   move: cx; rewrite (order_sum2_gle _ _ ora orb); move=> [_ [_ cs]].
   case cs; first (by intuition); case; last by intuition.
   by move=> [_ [bad _]].
have p4: is_segment c (substrate c) by apply substrate_is_segment.
have sc: substrate c = (canonical_du2 a b).
   rewrite /c order_sum2_substrate // !ordinal_p7//.
have aux: order_isomorphic (ordinal_o a) (induced_order c A).
  have ta: transf_axioms (fun z => (J z TPa)) a A.
    move=> z zs; rewrite /A; apply Z_inc; last by aw.
    rewrite sc canonical_du2_pr;aw; ee.
  exists (BL (fun z => (J z TPa)) a A).
  red; ee; aw.
    apply bl_bijective => //; first by move=> u v _ _; apply pr1_def.
    move=> y; rewrite Z_rw sc canonical_du2_pr.
    move=> [[py h] Qy]; case h; move=> [Py Qy'].
      by ex_tac; apply pair_exten; ee; aw.
    by elim two_points_distinct; rewrite -Qy -Qy'.
  move=> x y xs ys; aw; try apply ta => //.
  rewrite /c order_sum2_gle // (ordinal_p7 oa) (ordinal_p7 ob) -sc.
  apply iff_eq.
   move=> h; split; [by apply p2; apply ta | split; [by apply p2; apply ta |]].
   left; aw; ee.
  move=> [_ [_ ]]; aw;case; first by intuition.
  by case; [ move=> [_ [bad _]] | move => [_ bad]] ; elim bad.
have p5: order_isomorphic (induced_order c (substrate c)) (induced_order c A).
  rewrite induced_order_substrate //.
  move:(ordinal_p2 p1); rewrite -aoc => aux2.
  apply (orderIT aux2 aux).
move:(segments_iso1 p1 p2 p3 p4 p5) => Ac.
apply is_emptyset.
move=> y yb.
have: inc (J y TPb)(substrate c) by rewrite sc canonical_du2_pr; aw;ee.
by rewrite -Ac /A Z_rw; aw; move=> [_ bad]; elim two_points_distinctb.
Qed.

Lemma ord_double: forall x, is_ordinal x ->
  x *o 2o = x +o x.
Proof.
move=> x Bx.
rewrite - ord_11_2 (order_sum_distributive3 ordinal_1 ordinal_1 Bx).
by rewrite (ord_1_mult_unit_r Bx).
Qed.

Lemma ord_omega_non_zero: \omega <> 0o.
Proof.
move: inc0_Bnat;rewrite -[ord_omega]/Bnat.
move=> ob bad ;rewrite bad in ob; elim (emptyset_pr ob).
Qed.

Lemma order_sum_nc: let x := 1o in let y := \omega in
   x +o y <> y +o x.
Proof.
move=> x y; rewrite (ord_plus_int_omega inc1_Bnat) /y=> h.
move: (ordinal_a_ne_ab ordinal_omega ordinal_1 h).
apply card_one_not_zero.
Qed.

Lemma order_prod_nc: let x := 2o in let y := \omega in
   x *o y <> y *o x.
Proof.
move: card_two_not_zero => tnz.
move=> x y; rewrite (ord_mult_int_omega inc2_Bnat tnz).
rewrite (ord_double ordinal_omega) => h.
move: (ordinal_a_ne_ab ordinal_omega ordinal_omega h).
apply ord_omega_non_zero.
Qed.

Lemma order_sum_distr4:
  let a:= 1o in let b:= 1o in let c:= ord_omega in
  (a +o b) *o c <> (a *o c) +o (b *o c).
Proof.
move=> a b c.
move: card_one_not_zero => onz.
move: card_two_not_zero => tnz.
move: inc1_Bnat => ob.
rewrite (ord_mult_int_omega ob onz) (cardinal_ord_sum2 ob ob).
rewrite card_two_pr (ord_mult_int_omega inc2_Bnat tnz) => h.
move: (ordinal_a_ne_ab ordinal_omega ordinal_omega h).
apply ord_omega_non_zero.
Qed.

Lemma ord_succ_pr: forall x, is_ordinal x ->
  x +o 1o = succ_o x.
Proof.
move=> x xo; rewrite /ord_sum2.
move: (ordinal_succ xo) => sxo.
move: ordinal_1 => o1.
set aux:= (order_sum2 _ _).
have wo1: worder aux.
  by apply order_sum2_worder; apply ordinal_p10 => //.
apply ordinal_p9 => //.
set A:= substrate aux.
set B:= substrate (ordinal_o (succ_o x)).
have bv:succ_o x = B by symmetry; apply ordinal_p7.
have av: A = canonical_du2 x 1o.
  by rewrite /A order_sum2_substrate ?ordinal_p7 ; fprops.
have ab: TPb <> TPa by fprops.
have ta: transf_axioms (fun z : Set => Yo (Q z = TPa) (P z) x) A B.
  rewrite av -bv /succ_o;move=> z; rewrite canonical_du2_pr.
  move=> [_]; case; move=> [pz qz]; rewrite qz;Ytac0; fprops.
exists (BL (fun z => (Yo (Q z = TPa) (P z) x)) A B).
red; ee; aw.
  apply bl_bijective => //.
    move=> u v;rewrite av !canonical_du2_pr; move=> [up p1][vp p2].
    case p1=> [] [pa pb]; case p2=> [] [qa qb]; rewrite pb qb; Ytac0;Ytac0 => h.
    - apply pair_exten => //; ue.
    - rewrite h in pa; elim (ordinal_irreflexive xo pa).
    - rewrite -h in qa; elim (ordinal_irreflexive xo qa).
    - move: pa qa; rewrite /1o /card_one; aw.
      move => r1 r2; apply pair_exten => //; ue.
  rewrite av -bv /succ_o;move=> y; rewrite tack_on_rw; case.
    by move=> yw; exists (J y TPa); rewrite canonical_du2_pr; aw; ee;Ytac0.
  move => ->; exists (J emptyset TPb); rewrite canonical_du2_pr; aw; ee.
      right; rewrite /1o/card_one; fprops.
  by Ytac0.
have o2: order (ordinal_o x) by apply ordinal_p11.
have o3: order (ordinal_o 1o) by apply ordinal_p11.
move=> u v uA vA; rewrite (bl_W ta uA) (bl_W ta vA) /aux.
rewrite (order_sum2_gle _ _ o2 o3) (ordinal_p7 xo) (ordinal_p7 o1) -av.
rewrite/ordinal_o /gle ! subinclusion_order_rw.
rewrite /succ_o; apply iff_eq.
  move=> [_ [_ ]]; case; [ | case ];
  [move => [p1 [p2 p3]] | move => [p1 [p2 p3]] | move=> [p1 p2]];
   Ytac0; Ytac0; [ ee | ee |].
  move: uA; rewrite av canonical_du2_pr; move=> [_]; case; move=> [p3 p4].
    ee; apply (ordinal_transitive xo p3).
  by elim ab; ue.
move=> [h1 [h2 h3]]; ee.
move: uA vA h1 h2 h3;rewrite av !canonical_du2_pr; move=> [up p1][vp p2].
case p1=> [][pa p]; case p2 => [] [qa q]; rewrite p q; Ytac0; Ytac0 => h1 h2 h3.
- left; ee.
- right; right; ee.
- elim (ordinal_decent xo qa (h3 _ qa)).
- right; left; ee.
   move: pa; rewrite /1o/card_one; aw => ->; apply emptyset_sub_any.
Qed.

Lemma ord_succ_lt: forall a, is_ordinal a -> a <o (succ_o a).
Proof.
move=> a oa;rewrite ordinal_lt_pr0; ee.
  apply (ordinal_succ oa).
rewrite /succ_o; fprops.
Qed.

Lemma ord_lt_01: 0o <o 1o.
Proof.
rewrite ordinal_lt_pr0; ee.
    apply ordinal_0.
  apply ordinal_1.
rewrite /1o/card_one; fprops.
Qed.

Lemma ord_lt_12: 1o <o 2o.
Proof.
move: ordinal_1 => x1.
rewrite - ord_11_2 (ord_succ_pr x1); apply (ord_succ_lt x1).
Qed.

Lemma ord_lt_02: 0o <o 2o.
Proof. move: ord_lt_12 => [le _]; apply (ordinal_lt_le_trans ord_lt_01 le).
Qed.

Lemma succ_ordinal: forall a, is_ordinal a -> is_ordinal (a +o 1o).
Proof. by move=> a oa; move: ordinal_1 => oo; apply ord_sum2_ordinal.
Qed.

Hint Resolve ordinal_succ: fprops.

Lemma ord_lt_succ: forall a b, a <o (succ_o b) = a <=o b.
Proof.
move=> a b; rewrite ordinal_lt_pr0 ordinal_le_pr0.
apply iff_eq; move=> [p1 [p2 p3]].
  have bs: inc b (succ_o b) by rewrite /succ_o; fprops.
  move: (elt_of_ordinal p2 bs) => ob.
  by ee; rewrite (ordinal_sub4 p1 ob).
rewrite -(ordinal_sub4 p1 p2); ee.
Qed.

Lemma ord_succ_lt2: forall a b, a <o b = (succ_o a) <=o b.
Proof.
move=> a b; rewrite ordinal_lt_pr0 ordinal_le_pr0.
apply iff_eq; move=> [p1 [p2 p3]].
  ee. rewrite /succ_o; move => t; aw.
  case; [ apply (ordinal_transitive p2 p3) | by move=> -> ].
have asa: inc a (succ_o a) by rewrite /succ_o; fprops.
ee; apply (elt_of_ordinal p1 asa).
Qed.

Lemma ord2_lt_pr: forall x, 2o <=o x = 1o <o x.
Proof. by move=> x; rewrite /ord_two -succ_o_one -ord_succ_lt2. Qed.

Lemma ord2_trichotomy: forall x, is_ordinal x ->
  x = 0o \/ x= 1o \/ 2o <=o x.
Proof.
move=> x ox.
case (equal_or_not x 0o); first by left.
move=> xe; right.
case (equal_or_not x 1o); first by left.
move=> xee; right;rewrite ord2_lt_pr.
split; first apply zero_least_ordinal1; fprops.
Qed.

Lemma ord2_trichotomy1: forall x,
  2o <=o x -> (x <> 0o & x <> 1o).
Proof.
move=> x h; split.
    move=> xz; rewrite xz in h; move: (zero_least_ordinal3 h).
    apply card_two_not_zero.
  move => xz; move: h; rewrite xz ord2_lt_pr; by move=> [_].
Qed.

Lemma ord_succ_inj: forall a b, is_ordinal a -> is_ordinal b ->
   (succ_o a = succ_o b) -> a = b.
Proof.
rewrite /succ_o;move=> a b oa ob ss.
have: inc a (tack_on b b) by rewrite -ss; fprops.
have: inc b (tack_on a a) by rewrite ss; fprops.
aw; case => //; move=> h1 //;case => // => h2.
move: (ordinal_transitive oa h1 h2) => h3.
by elim (ordinal_decent oa h3).
Qed.

End Ordinals1.
Export Ordinals1.