Library MetaCoq.Template.Universes

From Coq Require Import MSetList MSetAVL MSetFacts MSetProperties MSetDecide.
From Equations Require Import Equations.
From MetaCoq.Template Require Import utils BasicAst config.
Require Import ssreflect.

Local Open Scope nat_scope.
Local Open Scope string_scope2.

Implicit Types (cf : checker_flags).

Ltac absurd :=
  match goal with
  | [H: ¬ True |- _] ⇒ elim (H I)
  | [H : False |- _] ⇒ elim H
  | H: is_true false |- _discriminate H
  | |- False _let H := fresh in intro H; elim H
  | |- _ False _let H := fresh in intros ? H; elim H
  | |- is_true false _let H := fresh in intro H; discriminate H
  | |- _ is_true false _let H := fresh in intros ? H; discriminate H
  end.
#[global]
Hint Extern 10 ⇒ absurd : core.

Valuations

A valuation is a universe level (nat) given for each universe variable (Level.t). It is >= for polymorphic concreteUniverses and > 0 for monomorphic concreteUniverses.
Record valuation :=
  { valuation_mono : string positive ;
    valuation_poly : nat nat }.

Class Evaluable (A : Type) := val : valuation A nat.

Levels are Set or Level or Var
Module Level.
  Inductive t_ : Set :=
  | lzero
  | Level (_ : string)
  | Var (_ : nat) .
  Derive NoConfusion for t_.

  Definition t := t_.

  Definition is_set (x : t) :=
    match x with
    | lzerotrue
    | _false
    end.

  Definition is_var (l : t) :=
    match l with
    | Var _true
    | _false
    end.

  Global Instance Evaluable : Evaluable t
    := fun v lmatch l with
               | lzero ⇒ (0%nat)
               | Level s ⇒ (Pos.to_nat (v.(valuation_mono) s))
               | Var x ⇒ (v.(valuation_poly) x)
               end.

  Definition compare (l1 l2 : t) : comparison :=
    match l1, l2 with
    | lzero, lzeroEq
    | lzero, _Lt
    | _, lzeroGt
    | Level s1, Level s2string_compare s1 s2
    | Level _, _Lt
    | _, Level _Gt
    | Var n, Var mNat.compare n m
    end.

  Definition eq : t t Prop := eq.
  Definition eq_equiv : Equivalence eq := _.

  Inductive lt_ : t t Prop :=
  | ltSetLevel s : lt_ lzero (Level s)
  | ltSetVar n : lt_ lzero (Var n)
  | ltLevelLevel s s' : StringOT.lt s s' lt_ (Level s) (Level s')
  | ltLevelVar s n : lt_ (Level s) (Var n)
  | ltVarVar n n' : Nat.lt n n' lt_ (Var n) (Var n').
  Derive Signature for lt_.

  Definition lt := lt_.

  Definition lt_strorder : StrictOrder lt.
  Proof.
    constructor.
    - intros [| |] X; inversion X.
      now eapply irreflexivity in H1.
      eapply Nat.lt_irrefl; tea.
    - intros [| |] [| |] [| |] X1 X2;
        inversion X1; inversion X2; constructor.
      now transitivity s0.
      etransitivity; tea.
  Qed.

  Definition lt_compat : Proper (eq ==> eq ==> iff) lt.
  Proof.
    intros x y e z t e'. unfold eq in *; subst. reflexivity.
  Qed.

  Definition compare_spec :
     x y : t, CompareSpec (x = y) (lt x y) (lt y x) (compare x y).
  Proof.
    intros [] []; repeat constructor.
    - eapply CompareSpec_Proper.
      5: eapply CompareSpec_string. 4: reflexivity.
      all: split; [now inversion 1|]. now intros →.
      all: intro; now constructor.
    - eapply CompareSpec_Proper.
      5: eapply Nat.compare_spec. 4: reflexivity.
      all: split; [now inversion 1|]. now intros →.
      all: intro; now constructor.
  Qed.

  Definition eqb (l1 l2 : Level.t) : bool
    := match compare l1 l2 with Eqtrue | _false end.

  Global Instance eqb_refl : Reflexive eqb.
  Proof.
    intros []; unfold eqb; cbnr.
    - rewrite (ssreflect.iffRL (string_compare_eq _ _)). all: auto. reflexivity.
    - rewrite Nat.compare_refl. reflexivity.
  Qed.

  Lemma eqb_spec l l' : reflect (eq l l') (eqb l l').
  Proof.
    destruct l, l'; cbn; try constructor; try reflexivity; try discriminate.
    - apply iff_reflect. unfold eqb; cbn.
      destruct (CompareSpec_string t0 t1); split; intro HH;
        try reflexivity; try discriminate; try congruence.
      all: inversion HH; subst; now apply irreflexivity in H.
    - apply iff_reflect. unfold eqb; cbn.
      destruct (Nat.compare_spec n n0); split; intro HH;
        try reflexivity; try discriminate; try congruence.
      all: inversion HH; subst; now apply Nat.lt_irrefl in H.
  Qed.

  Definition eq_level l1 l2 :=
    match l1, l2 with
    | Level.lzero, Level.lzerotrue
    | Level.Level s1, Level.Level s2ReflectEq.eqb s1 s2
    | Level.Var n1, Level.Var n2ReflectEq.eqb n1 n2
    | _, _false
    end.

  #[global, program] Instance reflect_level : ReflectEq Level.t := {
    eqb := eq_level
  }.
  Next Obligation.
    destruct x, y.
    all: unfold eq_level.
    all: try solve [ constructor ; reflexivity ].
    all: try solve [ constructor ; discriminate ].
    - destruct (ReflectEq.eqb_spec t0 t1) ; nodec.
      constructor. f_equal. assumption.
    - destruct (ReflectEq.eqb_spec n n0) ; nodec.
      constructor. subst. reflexivity.
  Defined.

  Definition eq_leibniz (x y : t) : eq x y x = y := id.

  Definition eq_dec : (l1 l2 : t), {l1 = l2}+{l1 l2} := Classes.eq_dec.

End Level.

Module LevelSet := MSetAVL.Make Level.
Module LevelSetFact := WFactsOn Level LevelSet.
Module LevelSetProp := WPropertiesOn Level LevelSet.
Module LevelSetDecide := WDecide (LevelSet).
Module LS := LevelSet.

Ltac lsets := LevelSetDecide.fsetdec.
Notation "(=_lset)" := LevelSet.Equal (at level 0).
Infix "=_lset" := LevelSet.Equal (at level 30).

Section LevelSetMoreFacts.

  Definition LevelSet_pair x y
    := LevelSet.add y (LevelSet.singleton x).

  Lemma LevelSet_pair_In x y z :
    LevelSet.In x (LevelSet_pair y z) x = y x = z.
  Proof.
    intro H. apply LevelSetFact.add_iff in H.
    destruct H; [intuition|].
    apply LevelSetFact.singleton_1 in H; intuition.
  Qed.

  Definition LevelSet_mem_union (s s' : LevelSet.t) x :
    LevelSet.mem x (LevelSet.union s s') LevelSet.mem x s LevelSet.mem x s'.
  Proof.
    rewrite LevelSetFact.union_b.
    apply orb_true_iff.
  Qed.

  Lemma LevelSet_In_fold_right_add x l :
    In x l LevelSet.In x (fold_right LevelSet.add LevelSet.empty l).
  Proof.
    split.
    - induction l; simpl ⇒ //.
      intros [<-|H].
      × eapply LevelSet.add_spec; left; auto.
      × eapply LevelSet.add_spec; right; auto.
    - induction l; simpl ⇒ //.
      × now rewrite LevelSetFact.empty_iff.
      × rewrite LevelSet.add_spec. intuition auto.
  Qed.

  Lemma LevelSet_union_empty s : LevelSet.Equal (LevelSet.union LevelSet.empty s) s.
  Proof.
    intros x; rewrite LevelSet.union_spec. lsets.
  Qed.
End LevelSetMoreFacts.

Module PropLevel.

  Inductive t := lSProp | lProp.
  Derive NoConfusion EqDec for t.

  Definition compare (l1 l2 : t) : comparison :=
    match l1, l2 with
    | lSProp, lSPropEq
    | lProp, lPropEq
    | lProp, lSPropGt
    | lSProp, lPropLt
    end.

  Inductive lt_ : t t Prop :=
    ltSPropProp : lt_ lSProp lProp.

  Definition lt := lt_.

  Global Instance lt_strorder : StrictOrder lt.
  split.
  - intros n X. destruct n;inversion X.
  - intros n1 n2 n3 X1 X2. destruct n1,n2,n3;auto; try inversion X1;try inversion X2.
  Defined.

End PropLevel.

Module LevelExpr.
  Definition t := (Level.t × nat)%type.

  Global Instance Evaluable : Evaluable t
    := fun v l ⇒ (snd l + val v (fst l)).

  Definition succ (l : t) := (fst l, S (snd l)).

  Definition get_level (e : t) : Level.t := fst e.

  Definition get_noprop (e : LevelExpr.t) := Some (fst e).

  Definition is_level (e : t) : bool :=
    match e with
    | (_, 0%nat)true
    | _false
    end.

  Definition make (l : Level.t) : t := (l, 0%nat).

  Definition set : t := (Level.lzero, 0%nat).
  Definition type1 : t := (Level.lzero, 1%nat).

  Definition from_kernel_repr (e : Level.t × bool) : t
    := (e.1, if e.2 then 1%nat else 0%nat).

  Definition to_kernel_repr (e : t) : Level.t × bool
    := match e with
       | (l, b)(l, match b with 0%natfalse | _true end)
       end.

  Definition eq : t t Prop := eq.

  Definition eq_equiv : Equivalence eq := _.

  Inductive lt_ : t t Prop :=
  | ltLevelExpr1 l n n' : (n < n')%nat lt_ (l, n) (l, n')
  | ltLevelExpr2 l l' b b' : Level.lt l l' lt_ (l, b) (l', b').

  Definition lt := lt_.

  Global Instance lt_strorder : StrictOrder lt.
  Proof.
    constructor.
    - intros x X; inversion X. subst. lia. subst.
      eapply Level.lt_strorder; eassumption.
    - intros x y z X1 X2; invs X1; invs X2; constructor; tea.
      etransitivity; tea.
      etransitivity; tea.
  Qed.

  Definition lt_compat : Proper (Logic.eq ==> Logic.eq ==> iff) lt.
    intros x x' H1 y y' H2; now rewrite H1 H2.
  Qed.

  Definition compare (x y : t) : comparison :=
    match x, y with
    | (l1, b1), (l2, b2)
      match Level.compare l1 l2 with
      | EqNat.compare b1 b2
      | xx
      end
    end.

  Definition compare_spec :
     x y : t, CompareSpec (x = y) (lt x y) (lt y x) (compare x y).
  Proof.
    intros [? ?] [? ?]; cbn; repeat constructor.
    destruct (Level.compare_spec t0 t1); repeat constructor; tas.
    subst.
    destruct (Nat.compare_spec n n0); repeat constructor; tas. congruence.
  Qed.

  Global Instance reflect_t : ReflectEq t := reflect_prod _ _ .

  Definition eq_dec : (l1 l2 : t), {l1 = l2} + {l1 l2} := Classes.eq_dec.

  Definition eq_leibniz (x y : t) : eq x y x = y := id.

  Lemma val_make v l
    : val v (LevelExpr.make l) = val v l.
  Proof.
    destruct l eqn:H; cbnr.
  Qed.

  Lemma val_make_npl v (l : Level.t)
    : val v (LevelExpr.make l) = val v l.
  Proof.
    destruct l; cbnr.
  Qed.

End LevelExpr.

Module LevelExprSet := MSetList.MakeWithLeibniz LevelExpr.
Module LevelExprSetFact := WFactsOn LevelExpr LevelExprSet.
Module LevelExprSetProp := WPropertiesOn LevelExpr LevelExprSet.

#[global, program] Instance levelexprset_reflect : ReflectEq LevelExprSet.t :=
  { eqb := LevelExprSet.equal }.
Next Obligation.
  destruct (LevelExprSet.equal x y) eqn:e; constructor.
  eapply LevelExprSet.equal_spec in e.
  now eapply LevelExprSet.eq_leibniz.
  intros e'.
  subst y.
  pose proof (@LevelExprSetFact.equal_1 x x).
  forward H. reflexivity. congruence.
Qed.

#[global] Instance levelexprset_eq_dec : Classes.EqDec LevelExprSet.t := Classes.eq_dec.

Record nonEmptyLevelExprSet
  := { t_set : LevelExprSet.t ;
       t_ne : LevelExprSet.is_empty t_set = false }.

Derive NoConfusion for nonEmptyLevelExprSet.

This coercion allows to see the non-empty set as a regular LevelExprSet.t
Coercion t_set : nonEmptyLevelExprSet >-> LevelExprSet.t.

Module NonEmptySetFacts.
  Definition singleton (e : LevelExpr.t) : nonEmptyLevelExprSet
    := {| t_set := LevelExprSet.singleton e;
          t_ne := eq_refl |}.

  Lemma not_Empty_is_empty s :
    ¬ LevelExprSet.Empty s LevelExprSet.is_empty s = false.
  Proof.
    intro H. apply not_true_is_false. intro H'.
    apply H. now apply LevelExprSetFact.is_empty_2 in H'.
  Qed.

  Program Definition add (e : LevelExpr.t) (u : nonEmptyLevelExprSet) : nonEmptyLevelExprSet
    := {| t_set := LevelExprSet.add e u |}.
  Next Obligation.
    apply not_Empty_is_empty; intro H.
    eapply H. eapply LevelExprSet.add_spec.
    left; reflexivity.
  Qed.

  Lemma add_spec e u e' :
    LevelExprSet.In e' (add e u) e' = e LevelExprSet.In e' u.
  Proof.
    apply LevelExprSet.add_spec.
  Qed.

  Definition add_list : list LevelExpr.t nonEmptyLevelExprSet nonEmptyLevelExprSet
    := List.fold_left (fun u eadd e u).

  Lemma add_list_spec l u e :
    LevelExprSet.In e (add_list l u) In e l LevelExprSet.In e u.
  Proof.
    unfold add_list. rewrite <- fold_left_rev_right.
    etransitivity. 2:{ eapply or_iff_compat_r. etransitivity.
                       2: apply @InA_In_eq with (A:=LevelExpr.t).
                       eapply InA_rev. }
    induction (List.rev l); cbn.
    - split. intuition. intros [H|H]; tas. invs H.
    - split.
      + intro H. apply add_spec in H. destruct H as [H|H].
        × left. now constructor.
        × apply IHl0 in H. destruct H as [H|H]; [left|now right].
          now constructor 2.
      + intros [H|H]. inv H.
        × apply add_spec; now left.
        × apply add_spec; right. apply IHl0. now left.
        × apply add_spec; right. apply IHl0. now right.
  Qed.

  Program Definition to_nonempty_list (u : nonEmptyLevelExprSet) : LevelExpr.t × list LevelExpr.t
    := match LevelExprSet.elements u with
       | []False_rect _ _
       | e :: l(e, l)
       end.
  Next Obligation.
    destruct u as [u1 u2]; cbn in ×. revert u2.
    apply eq_true_false_abs.
    unfold LevelExprSet.is_empty, LevelExprSet.Raw.is_empty,
    LevelExprSet.elements, LevelExprSet.Raw.elements in ×.
    rewrite <- Heq_anonymous; reflexivity.
  Qed.

  Lemma singleton_to_nonempty_list e : to_nonempty_list (singleton e) = (e, []).
  Proof. reflexivity. Defined.

  Lemma to_nonempty_list_spec u :
    let '(e, u') := to_nonempty_list u in
    e :: u' = LevelExprSet.elements u.
  Proof.
    destruct u as [u1 u2].
    unfold to_nonempty_list; cbn.
    set (l := LevelExprSet.elements u1). unfold l at 2 3 4.
    set (e := (eq_refl: l = LevelExprSet.elements u1)); clearbody e.
    destruct l.
    - exfalso. revert u2. apply eq_true_false_abs.
      unfold LevelExprSet.is_empty, LevelExprSet.Raw.is_empty,
      LevelExprSet.elements, LevelExprSet.Raw.elements in ×.
      rewrite <- e; reflexivity.
    - reflexivity.
  Qed.

  Lemma to_nonempty_list_spec' u :
    (to_nonempty_list u).1 :: (to_nonempty_list u).2 = LevelExprSet.elements u.
  Proof.
    pose proof (to_nonempty_list_spec u).
    now destruct (to_nonempty_list u).
  Qed.

  Lemma In_to_nonempty_list (u : nonEmptyLevelExprSet) (e : LevelExpr.t) :
    LevelExprSet.In e u
     e = (to_nonempty_list u).1 In e (to_nonempty_list u).2.
  Proof.
    etransitivity. symmetry. apply LevelExprSet.elements_spec1.
    pose proof (to_nonempty_list_spec' u) as H.
    destruct (to_nonempty_list u) as [e' l]; cbn in ×.
    rewrite <- H; clear. etransitivity. apply InA_cons.
    eapply or_iff_compat_l. apply InA_In_eq.
  Qed.

  Lemma In_to_nonempty_list_rev (u : nonEmptyLevelExprSet) (e : LevelExpr.t) :
    LevelExprSet.In e u
     e = (to_nonempty_list u).1 In e (List.rev (to_nonempty_list u).2).
  Proof.
    etransitivity. eapply In_to_nonempty_list.
    apply or_iff_compat_l. apply in_rev.
  Qed.

  Definition map (f : LevelExpr.t LevelExpr.t) (u : nonEmptyLevelExprSet) : nonEmptyLevelExprSet :=
    let '(e, l) := to_nonempty_list u in
    add_list (List.map f l) (singleton (f e)).

  Lemma map_spec f u e :
    LevelExprSet.In e (map f u) e0, LevelExprSet.In e0 u e = (f e0).
  Proof.
    unfold map. symmetry. etransitivity.
    { eapply iff_ex; intro. eapply and_iff_compat_r. eapply In_to_nonempty_list. }
    destruct (to_nonempty_list u) as [e' l]; cbn in ×.
    symmetry. etransitivity. eapply add_list_spec.
    etransitivity. eapply or_iff_compat_l. apply LevelExprSet.singleton_spec.
    etransitivity. eapply or_iff_compat_r.
    apply in_map_iff. clear u. split.
    - intros [[e0 []]|H].
      + e0. split. right; tas. congruence.
      + e'. split; tas. left; reflexivity.
    - intros [xx [[H|H] ?]].
      + right. congruence.
      + left. xx. split; tas; congruence.
  Qed.

  Program Definition non_empty_union (u v : nonEmptyLevelExprSet) : nonEmptyLevelExprSet :=
    {| t_set := LevelExprSet.union u v |}.
  Next Obligation.
    apply not_Empty_is_empty; intro H.
    assert (HH: LevelExprSet.Empty u). {
      intros x Hx. apply (H x).
      eapply LevelExprSet.union_spec. now left. }
    apply LevelExprSetFact.is_empty_1 in HH.
    rewrite t_ne in HH; discriminate.
  Qed.

  Lemma elements_not_empty (u : nonEmptyLevelExprSet) : LevelExprSet.elements u [].
  Proof.
    destruct u as [u1 u2]; cbn; intro e.
    unfold LevelExprSet.is_empty, LevelExprSet.elements,
    LevelExprSet.Raw.elements in ×.
    rewrite e in u2; discriminate.
  Qed.

  Lemma eq_univ (u v : nonEmptyLevelExprSet) :
    u = v :> LevelExprSet.t u = v.
  Proof.
    destruct u as [u1 u2], v as [v1 v2]; cbn. intros X; destruct X.
    now rewrite (uip_bool _ _ u2 v2).
  Qed.

  Lemma eq_univ' (u v : nonEmptyLevelExprSet) :
    LevelExprSet.Equal u v u = v.
  Proof.
    intro H. now apply eq_univ, LevelExprSet.eq_leibniz.
  Qed.

  Lemma eq_univ'' (u v : nonEmptyLevelExprSet) :
    LevelExprSet.elements u = LevelExprSet.elements v u = v.
  Proof.
    intro H. apply eq_univ.
    destruct u as [u1 u2], v as [v1 v2]; cbn in *; clear u2 v2.
    destruct u1 as [u1 u2], v1 as [v1 v2]; cbn in ×.
    destruct H. now rewrite (uip_bool _ _ u2 v2).
  Qed.

  Lemma univ_expr_eqb_true_iff (u v : nonEmptyLevelExprSet) :
    LevelExprSet.equal u v u = v.
  Proof.
    split.
    - intros.
      apply eq_univ'. now apply LevelExprSet.equal_spec.
    - intros →. now apply LevelExprSet.equal_spec.
  Qed.

  Lemma univ_expr_eqb_comm (u v : nonEmptyLevelExprSet) :
    LevelExprSet.equal u v LevelExprSet.equal v u.
  Proof.
    transitivity (u = v). 2: transitivity (v = u).
    - apply univ_expr_eqb_true_iff.
    - split; apply eq_sym.
    - split; apply univ_expr_eqb_true_iff.
  Qed.

  Lemma LevelExprSet_for_all_false f u :
    LevelExprSet.for_all f u = false LevelExprSet.exists_ (negb f) u.
  Proof.
    intro H. rewrite LevelExprSetFact.exists_b.
    rewrite LevelExprSetFact.for_all_b in H.
    all: try now intros x y [].
    induction (LevelExprSet.elements u); cbn in *; [discriminate|].
    apply andb_false_iff in H; apply orb_true_iff; destruct H as [H|H].
    left; now rewrite H.
    right; now rewrite IHl.
  Qed.

  Lemma LevelExprSet_For_all_exprs (P : LevelExpr.t Prop) (u : nonEmptyLevelExprSet)
    : LevelExprSet.For_all P u
       P (to_nonempty_list u).1 Forall P (to_nonempty_list u).2.
  Proof.
    etransitivity.
    - eapply iff_forall; intro e. eapply imp_iff_compat_r.
      apply In_to_nonempty_list.
    - cbn; split.
      + intro H. split. apply H. now left.
        apply Forall_forall. intros x H0. apply H; now right.
      + intros [H1 H2] e [He|He]. subst e; tas.
        eapply Forall_forall in H2; tea.
  Qed.

End NonEmptySetFacts.
Import NonEmptySetFacts.

Module LevelAlgExpr.
A universe / an algebraic expression is a list of universe expressions which is:
  • sorted
  • without duplicate
  • non empty

  Definition t := nonEmptyLevelExprSet.

  #[global, program] Instance levelexprset_reflect : ReflectEq t :=
    { eqb x y := eqb x.(t_set) y.(t_set) }.
  Next Obligation.
    destruct (eqb_spec (t_set x) (t_set y)); constructor.
    destruct x, y; cbn in ×. subst.
    now rewrite (uip t_ne0 t_ne1).
    intros e; subst x; apply H.
    reflexivity.
  Qed.

  #[global] Instance eq_dec_univ0 : EqDec t := eq_dec.

  Definition make (e: LevelExpr.t) : t := singleton e.
  Definition make' (l: Level.t) := singleton (LevelExpr.make l).

The non empty / sorted / without dup list of univ expressions, the components of the pair are the head and the tail of the (non empty) list
  Definition exprs : t LevelExpr.t × list LevelExpr.t := to_nonempty_list.

  Global Instance Evaluable : Evaluable LevelAlgExpr.t
    := fun v u
      let '(e, u) := LevelAlgExpr.exprs u in
      List.fold_left (fun n eNat.max (val v e) n) u (val v e).

Test if the universe is a lub of levels or contains +n's.
  Definition is_levels (u : t) : bool :=
    LevelExprSet.for_all LevelExpr.is_level u.

Test if the universe is a level or an algebraic universe.
  Definition is_level (u : t) : bool :=
    (LevelExprSet.cardinal u =? 1)%nat && is_levels u.

  Definition succ : t t := map LevelExpr.succ.

The l.u.b. of 2 non-prop universe sets
  Definition sup : t t t := non_empty_union.

  Definition get_is_level (u : t) : option Level.t :=
    match LevelExprSet.elements u with
    | [(l, 0%nat)]Some l
    | _None
    end.

  Lemma val_make v e : val v (make e) = val v e.
  Proof. reflexivity. Qed.

  Lemma val_make' v l
    : val v (make' l) = val v l.
  Proof. reflexivity. Qed.

End LevelAlgExpr.

Ltac u :=
  change LevelSet.elt with Level.t in *;
  change LevelExprSet.elt with LevelExpr.t in ×.

Lemma val_fold_right (u : LevelAlgExpr.t) v :
  val v u = fold_right (fun e xNat.max (val v e) x) (val v (LevelAlgExpr.exprs u).1)
                       (List.rev (LevelAlgExpr.exprs u).2).
Proof.
  unfold val at 1, LevelAlgExpr.Evaluable.
  destruct (LevelAlgExpr.exprs u).
  now rewrite fold_left_rev_right.
Qed.

Lemma val_In_le (u : LevelAlgExpr.t) v e :
  LevelExprSet.In e u val v e val v u.
Proof.
  intro H. rewrite val_fold_right.
  apply In_to_nonempty_list_rev in H.
  fold LevelAlgExpr.exprs in H; destruct (LevelAlgExpr.exprs u); cbn in ×.
  destruct H as [H|H].
  - subst. induction (List.rev l); cbnr. lia.
  - induction (List.rev l); cbn; invs H.
    + u; lia.
    + specialize (IHl0 H0). lia.
Qed.

Lemma val_In_max (u : LevelAlgExpr.t) v :
   e, LevelExprSet.In e u val v e = val v u.
Proof.
  eapply iff_ex. {
    intro. eapply and_iff_compat_r. apply In_to_nonempty_list_rev. }
  rewrite val_fold_right. fold LevelAlgExpr.exprs; destruct (LevelAlgExpr.exprs u) as [e l]; cbn in ×.
  clear. induction (List.rev l); cbn.
  - e. split; cbnr. left; reflexivity.
  - destruct IHl0 as [e' [H1 H2]].
    destruct (Nat.max_dec (val v a) (fold_right (fun e0 x0Nat.max (val v e0) x0)
                                               (val v e) l0)) as [H|H];
      rewrite H; clear H.
    + a. split; cbnr. right. now constructor.
    + rewrite <- H2. e'. split; cbnr.
      destruct H1 as [H1|H1]; [now left|right]. now constructor 2.
Qed.

Lemma val_ge_caract (u : LevelAlgExpr.t) v k :
  ( e, LevelExprSet.In e u val v e k) val v u k.
Proof.
  split.
  - eapply imp_iff_compat_r. {
      eapply iff_forall; intro. eapply imp_iff_compat_r.
      apply In_to_nonempty_list_rev. }
    rewrite val_fold_right.
    fold LevelAlgExpr.exprs; destruct (LevelAlgExpr.exprs u) as [e l]; cbn; clear.
    induction (List.rev l); cbn.
    + intros H. apply H. left; reflexivity.
    + intros H.
      destruct (Nat.max_dec (val v a) (fold_right (fun e0 xNat.max (val v e0) x)
                                                 (val v e) l0)) as [H'|H'];
        rewrite H'; clear H'.
      × apply H. right. now constructor.
      × apply IHl0. intros x [H0|H0]; apply H. now left.
        right; now constructor 2.
  - intros H e He. eapply val_In_le in He. etransitivity; eassumption.
Qed.

Lemma val_le_caract (u : LevelAlgExpr.t) v k :
  ( e, LevelExprSet.In e u k val v e) k val v u.
Proof.
  split.
  - eapply imp_iff_compat_r. {
      eapply iff_ex; intro. eapply and_iff_compat_r. apply In_to_nonempty_list_rev. }
    rewrite val_fold_right.
    fold LevelAlgExpr.exprs; destruct (LevelAlgExpr.exprs u) as [e l]; cbn; clear.
    induction (List.rev l); cbn.
    + intros H. destruct H as [e' [[H1|H1] H2]].
      × now subst.
      × invs H1.
    + intros [e' [[H1|H1] H2]].
      × forward IHl0; [|lia]. e'. split; tas. left; assumption.
      × invs H1.
        -- u; lia.
        -- forward IHl0; [|lia]. e'. split; tas. right; assumption.
  - intros H. destruct (val_In_max u v) as [e [H1 H2]].
     e. rewrite H2; split; assumption.
Qed.

Lemma val_caract (u : LevelAlgExpr.t) v k :
  val v u = k
   ( e, LevelExprSet.In e u val v e k)
     e, LevelExprSet.In e u val v e = k.
Proof.
  split.
  - intros <-; split.
    + apply val_In_le.
    + apply val_In_max.
  - intros [H1 H2].
    apply val_ge_caract in H1.
    assert (k val v u); [clear H1|lia].
    apply val_le_caract. destruct H2 as [e [H2 H2']].
     e. split; tas. lia.
Qed.

Lemma val_add v e (s: LevelAlgExpr.t)
  : val v (add e s) = Nat.max (val v e) (val v s).
Proof.
  apply val_caract. split.
  - intros e' H. apply LevelExprSet.add_spec in H. destruct H as [H|H].
    + subst. u; lia.
    + eapply val_In_le with (v:=v) in H. lia.
  - destruct (Nat.max_dec (val v e) (val v s)) as [H|H]; rewrite H; clear H.
    + e. split; cbnr. apply LevelExprSetFact.add_1. reflexivity.
    + destruct (val_In_max s v) as [e' [H1 H2]].
       e'. split; tas. now apply LevelExprSetFact.add_2.
Qed.

Lemma val_sup v s1 s2 :
  val v (LevelAlgExpr.sup s1 s2) = Nat.max (val v s1) (val v s2).
Proof.
  eapply val_caract. cbn. split.
  - intros e' H. eapply LevelExprSet.union_spec in H. destruct H as [H|H].
    + eapply val_In_le with (v:=v) in H. lia.
    + eapply val_In_le with (v:=v) in H. lia.
  - destruct (Nat.max_dec (val v s1) (val v s2)) as [H|H]; rewrite H; clear H.
    + destruct (val_In_max s1 v) as [e' [H1 H2]].
       e'. split; tas. apply LevelExprSet.union_spec. now left.
    + destruct (val_In_max s2 v) as [e' [H1 H2]].
       e'. split; tas. apply LevelExprSet.union_spec. now right.
Qed.

Ltac proper := let H := fresh in try (intros ? ? H; destruct H; reflexivity).

Lemma for_all_elements (P : LevelExpr.t bool) u :
  LevelExprSet.for_all P u = forallb P (LevelExprSet.elements u).
Proof.
  apply LevelExprSetFact.for_all_b; proper.
Qed.

Lemma levelalg_get_is_level_correct u l :
  LevelAlgExpr.get_is_level u = Some l u = LevelAlgExpr.make' l.
Proof.
  intro H.
  unfold LevelAlgExpr.get_is_level in ×.
  destruct (LevelExprSet.elements u) as [|l0 L] eqn:Hu1; [discriminate |].
  destruct l0, L; try discriminate.
  × destruct n; inversion H; subst.
    apply eq_univ''; apply Hu1.
  × destruct n; discriminate.
Qed.

Lemma sup0_comm x1 x2 :
  LevelAlgExpr.sup x1 x2 = LevelAlgExpr.sup x2 x1.
Proof.
  apply eq_univ'; simpl. unfold LevelExprSet.Equal.
  intros H. rewrite !LevelExprSet.union_spec. intuition.
Qed.

Declare Scope univ_scope.
Delimit Scope univ_scope with u.

Universes with linear hierarchy
Section ConcreteUniverses.
  Context {cf}.

  Inductive concreteUniverses :=
    | UProp
    | USProp
    | UType (i : nat).
  Derive NoConfusion EqDec for concreteUniverses.

u + n <= u'
  Definition leq_cuniverse_n n u u' :=
    match u, u' with
    | UProp, UProp
    | USProp, USProp ⇒ (n = 0)%Z
    | UType u, UType u' ⇒ (Z.of_nat u Z.of_nat u' - n)%Z
    | UProp, UType u
      if prop_sub_type then True else False
    | _, _False
    end.

  Definition leq_cuniverse := leq_cuniverse_n 0.
  Definition lt_cuniverse := leq_cuniverse_n 1.

  Notation "x <_ n y" := (leq_cuniverse_n n x y) (at level 10, n name) : univ_scope.
  Notation "x < y" := (lt_cuniverse x y) : univ_scope.
  Notation "x <= y" := (leq_cuniverse x y) : univ_scope.

  Definition cuniv_sup u1 u2 :=
    match u1, u2 with
    | UProp, UPropUProp
    | USProp, USPropUSProp
    | UType v, UType v'UType (Nat.max v v')
    | _, UType _u2
    | UType _, _u1
    | UProp, USPropUProp
    | USProp, UPropUProp
    end.

  Definition is_uprop u := match u with UPropTrue | _False end.
  Definition is_usprop u := match u with USPropTrue | _False end.
  Definition is_uproplevel u := match u with USProp | UPropTrue | _False end.
  Definition is_uproplevel_or_set u := match u with USProp | UProp | UType 0 ⇒ True | _False end.
  Definition is_utype u := match u with UType _True | _False end.

Type of a product
  Definition cuniv_of_product domsort rangsort :=
    match rangsort with
    
    | UProp | USProprangsort
    | _cuniv_sup domsort rangsort
    end.

  Lemma cuniv_sup_comm u u' : cuniv_sup u u' = cuniv_sup u' u.
  Proof using Type. destruct u, u'; cbn; try congruence. f_equal; lia. Qed.

  Lemma cuniv_sup_not_uproplevel u u' :
    ¬ is_uproplevel u n, cuniv_sup u u' = UType n.
  Proof using Type.
    destruct u, u'; cbn; intros Hp; try absurd;
    now eexists.
  Qed.

  Lemma cuniv_le_uprop_inv u : (u UProp)%u u = UProp.
  Proof using Type. destruct u; simpl; intros Hle; try absurd; now reflexivity. Qed.

  Lemma cuniv_le_usprop_inv u : (u USProp)%u u = USProp.
  Proof using Type. destruct u; simpl; intros Hle; try absurd; now reflexivity. Qed.

  Lemma cuniv_uprop_le_inv u : (UProp u)%u
    (u = UProp (prop_sub_type n, u = UType n)).
  Proof using Type.
    destruct u; simpl; intros Hle; [ left; reflexivity | absurd | right ].
    destruct prop_sub_type; [| absurd].
    split; [ trivial | now eexists ].
  Qed.

  Lemma cuniv_sup_mon u u' v v' : (u u')%u (UType v UType v')%u
    (cuniv_sup u (UType v) cuniv_sup u' (UType v'))%u.
  Proof using Type.
    destruct u, u'; simpl; intros Hle Hle'; try absurd;
    lia.
  Qed.

  Lemma leq_cuniv_of_product_mon u u' v v' :
    (u u')%u
    (v v')%u
    (cuniv_of_product u v cuniv_of_product u' v')%u.
  Proof using Type.
    intros Hle1 Hle2.
    destruct v, v'; cbn in Hle2 |- *; auto.
    - destruct u'; cbn; assumption.
    - apply cuniv_sup_mon; assumption.
  Qed.

  Lemma impredicative_cuniv_product {l u} :
    is_uproplevel u
    (cuniv_of_product l u u)%u.
  Proof using Type. now destruct u. Qed.

  Global Instance leq_cuniverse_refl : Reflexive leq_cuniverse.
  Proof using Type.
    intros []; cbnr; lia.
  Qed.

  Global Instance leq_cuniverse_n_trans n : Transitive (leq_cuniverse_n (Z.of_nat n)).
  Proof using Type.
    intros [] [] []; cbnr; trivial; try absurd; lia.
  Qed.

  Global Instance leq_cuniverse_trans : Transitive leq_cuniverse.
  Proof using Type. apply (leq_cuniverse_n_trans 0). Qed.

  Global Instance lt_cuniverse_trans : Transitive lt_cuniverse.
  Proof using Type. apply (leq_cuniverse_n_trans 1). Qed.

  Global Instance leq_cuniverse_preorder : PreOrder leq_cuniverse :=
    Build_PreOrder _ _ _.

  Global Instance lt_cuniverse_str_order : StrictOrder lt_cuniverse.
  Proof using Type.
    split.
    - intros []; unfold complement; cbnr; lia.
    - exact _.
  Qed.

  Global Instance leq_cuniverse_antisym : Antisymmetric _ eq leq_cuniverse.
  Proof using Type.
    intros [] []; cbnr; try absurd.
    intros. f_equal; lia.
  Qed.

End ConcreteUniverses.

This inductive classifies which eliminations are allowed for inductive types in various sorts.
Inductive allowed_eliminations : Set :=
  | IntoSProp
  | IntoPropSProp
  | IntoSetPropSProp
  | IntoAny.
Derive NoConfusion EqDec for allowed_eliminations.

Definition is_allowed_elimination_cuniv (allowed : allowed_eliminations) : concreteUniverses Prop :=
  match allowed with
  | IntoSPropis_usprop
  | IntoPropSPropis_uproplevel
  | IntoSetPropSPropis_uproplevel_or_set
  | IntoAnyfun _True
  end.

Module Universe.
  Inductive t_ :=
    lProp | lSProp | lType (_ : LevelAlgExpr.t).
  Derive NoConfusion for t_.

  Definition t := t_.

  Definition eqb (u1 u2 : t) : bool :=
    match u1, u2 with
    | lSProp, lSProptrue
    | lProp, lProptrue
    | lType e1, lType e2eqb e1 e2
    | _, _false
    end.

  #[global, program] Instance reflect_eq_universe : ReflectEq t :=
    { eqb := eqb }.
  Next Obligation.
    destruct x, y; cbn; try constructor; auto; try congruence.
    destruct (eqb_spec t0 t1); constructor. now f_equal.
    congruence.
  Qed.

  #[global] Instance eq_dec_univ : EqDec t := eq_dec.

  Definition on_sort (P: LevelAlgExpr.t Prop) (def: Prop) (s : t) :=
    match s with
    | lProp | lSPropdef
    | lType lP l
    end.

Create a universe representing the given level.
  Definition make (l : Level.t) : t :=
    lType (LevelAlgExpr.make (LevelExpr.make l)).

  Definition of_expr e := (lType (LevelAlgExpr.make e)).

  Definition add_to_exprs (e : LevelExpr.t) (u : t) : t :=
    match u with
    | lSProp | lPropu
    | lType llType (add e l)
    end.

  Definition add_list_to_exprs (es : list LevelExpr.t) (u : t) : t :=
    match u with
    | lSProp | lPropu
    | lType llType (add_list es l)
    end.

Test if the universe is a lub of levels or contains +n's.
  Definition is_levels (u : t) : bool :=
    match u with
    | lSProp | lProptrue
    | lType lLevelAlgExpr.is_levels l
    end.

Test if the universe is a level or an algebraic universe.
  Definition is_level (u : t) : bool :=
    match u with
    | lSProp | lProptrue
    | lType lLevelAlgExpr.is_level l
    end.

  Definition is_sprop (u : t) : bool :=
    match u with
      | lSProptrue
      | _false
    end.

  Definition is_prop (u : t) : bool :=
    match u with
      | lProptrue
      | _false
    end.

  Definition is_type_sort (u : t) : bool :=
    match u with
      | lType _true
      | _false
    end.

  Definition type0 : t := make Level.lzero.
  Definition type1 : t := lType (LevelAlgExpr.make LevelExpr.type1).

  Definition of_levels (l : PropLevel.t + Level.t) : t :=
    match l with
    | inl PropLevel.lSProplSProp
    | inl PropLevel.lProplProp
    | inr llType (LevelAlgExpr.make' l)
    end.

  Definition from_kernel_repr (e : Level.t × bool) (es : list (Level.t × bool)) : t
    := lType (add_list (List.map LevelExpr.from_kernel_repr es)
                (LevelAlgExpr.make (LevelExpr.from_kernel_repr e))).



The universe strictly above FOR TYPING (not cumulativity)

  Definition super (l : t) : t :=
    match l with
    | lSProptype1
    | lProptype1
    | lType llType (LevelAlgExpr.succ l)
    end.

  Definition sup (u v : t) : t :=
    match u,v with
    | lSProp, lProplProp
    | lProp, lSProplProp
    | (lSProp | lProp), u'u'
    | u', (lSProp | lProp) ⇒ u'
    | lType l1, lType l2lType (LevelAlgExpr.sup l1 l2)
    end.

  Definition get_univ_exprs (u : t) (H1 : is_prop u = false) (H2 : is_sprop u = false) : LevelAlgExpr.t.
  destruct u; try discriminate; easy. Defined.

Type of a product
  Definition sort_of_product (domsort rangsort : t) :=
    
    if Universe.is_prop rangsort || Universe.is_sprop rangsort then rangsort
    else Universe.sup domsort rangsort.

  Lemma eqb_refl u : eqb u u.
  Proof.
    destruct u; auto.
    now apply LevelExprSet.equal_spec.
  Qed.

  Definition get_is_level (u : t) : option Level.t :=
    match u with
    | lSPropNone
    | lPropNone
    | lType lLevelAlgExpr.get_is_level l
    end.

  Definition to_cuniv v u :=
    match u with
    | lSPropUSProp
    | lPropUProp
    | lType lUType (val v l)
    end.

  Lemma val_make v l
    : to_cuniv v (make l) = UType (val v l).
  Proof.
    destruct l; cbnr.
  Qed.

  Lemma make_inj x y :
    make x = make y x = y.
  Proof.
    destruct x, y; try now inversion 1.
  Qed.

End Universe.

Definition is_propositional u :=
  Universe.is_prop u || Universe.is_sprop u.

Notation "⟦ u ⟧_ v" := (Universe.to_cuniv v u) (at level 0, format "⟦ u ⟧_ v", v name) : univ_scope.

Lemma val_universe_sup v u1 u2 :
  Universe.to_cuniv v (Universe.sup u1 u2) = cuniv_sup (Universe.to_cuniv v u1) (Universe.to_cuniv v u2).
Proof.
  destruct u1 as [ | | l1]; destruct u2 as [ | | l2];cbn;try lia; auto.
  f_equal. apply val_sup.
Qed.

Lemma is_prop_val u :
  Universe.is_prop u v, Universe.to_cuniv v u = UProp.
Proof.
  destruct u as [| | u];try discriminate;auto.
Qed.

Lemma is_sprop_val u :
  Universe.is_sprop u v, Universe.to_cuniv v u = USProp.
Proof.
  destruct u as [| | u];try discriminate;auto.
Qed.


Lemma val_is_prop u v :
  Universe.to_cuniv v u = UProp Universe.is_prop u.
Proof.
  destruct u; auto;cbn in *; intuition congruence.
Qed.

Lemma val_is_sprop u v :
  Universe.to_cuniv v u = USProp Universe.is_sprop u.
Proof.
  destruct u;auto;cbn in *; intuition congruence.
Qed.

Lemma is_prop_and_is_sprop_val_false u :
  Universe.is_prop u = false Universe.is_sprop u = false
   v, n, Universe.to_cuniv v u = UType n.
Proof.
  intros Hp Hsp v.
  destruct u; try discriminate. simpl. eexists; eauto.
Qed.

Lemma val_is_prop_false u v n :
  Universe.to_cuniv v u = UType n Universe.is_prop u = false.
Proof.
  pose proof (is_prop_val u) as H. destruct (Universe.is_prop u); cbnr.
  rewrite (H eq_refl v). discriminate.
Qed.

Lemma get_is_level_correct u l :
  Universe.get_is_level u = Some l u = Universe.lType (LevelAlgExpr.make' l).
Proof.
  intro H; destruct u; cbnr; try discriminate.
  f_equal; now apply levelalg_get_is_level_correct.
Qed.

Lemma eqb_true_iff u v :
  Universe.eqb u v u = v.
Proof.
  split. 2: intros ->; apply Universe.eqb_refl.
  intro H.
  destruct u, v;auto;try discriminate.
  apply f_equal. now apply univ_expr_eqb_true_iff.
Qed.

Lemma sup_comm x1 x2 :
  Universe.sup x1 x2 = Universe.sup x2 x1.
Proof.
  destruct x1,x2;auto.
  cbn;apply f_equal;apply sup0_comm.
Qed.

Lemma is_not_prop_and_is_not_sprop u :
  Universe.is_prop u = false Universe.is_sprop u = false
   u', u = Universe.lType u'.
Proof.
  intros Hp Hsp.
  destruct u; try discriminate. now eexists.
Qed.

Lemma is_prop_sort_sup x1 x2 :
  Universe.is_prop (Universe.sup x1 x2)
   Universe.is_prop x2 Universe.is_sprop x2 .
Proof. destruct x1,x2;auto. Qed.

Lemma is_prop_sort_sup' x1 x2 :
  Universe.is_prop (Universe.sup x1 x2)
   Universe.is_prop x1 Universe.is_sprop x1 .
Proof. destruct x1,x2;auto. Qed.

Lemma is_prop_or_sprop_sort_sup x1 x2 :
  Universe.is_sprop (Universe.sup x1 x2) Universe.is_sprop x2.
Proof. destruct x1,x2;auto. Qed.

Lemma is_prop_sort_sup_prop x1 x2 :
  Universe.is_prop x1 && Universe.is_prop x2
  Universe.is_prop (Universe.sup x1 x2).
Proof. destruct x1,x2;auto. Qed.

Lemma is_prop_or_sprop_sort_sup_prop x1 x2 :
  Universe.is_sprop x1 && Universe.is_sprop x2
  Universe.is_sprop (Universe.sup x1 x2).
Proof. destruct x1,x2;auto. Qed.

Lemma is_prop_sup u s :
  Universe.is_prop (Universe.sup u s)
  (Universe.is_prop u Universe.is_sprop u)
  (Universe.is_prop s Universe.is_sprop s).
Proof. destruct u,s;auto. Qed.

Lemma is_sprop_sup_iff u s :
  Universe.is_sprop (Universe.sup u s)
  (Universe.is_sprop u Universe.is_sprop s).
Proof. split;destruct u,s;intuition. Qed.

Lemma is_type_sup_r s1 s2 :
  Universe.is_type_sort s2
  Universe.is_type_sort (Universe.sup s1 s2).
Proof. destruct s2; try absurd; destruct s1; cbnr; intros; absurd. Qed.

Lemma is_prop_sort_prod x2 x3 :
  Universe.is_prop (Universe.sort_of_product x2 x3)
   Universe.is_prop x3.
Proof.
  unfold Universe.sort_of_product.
  destruct x3;cbn;auto.
  intros;simpl in *;destruct x2;auto.
Qed.

Lemma is_sprop_sort_prod x2 x3 :
  Universe.is_sprop (Universe.sort_of_product x2 x3)
   Universe.is_sprop x3.
Proof.
  unfold Universe.sort_of_product.
  destruct x3;cbn;auto.
  intros;simpl in *;destruct x2;auto.
Qed.

Module ConstraintType.
  Inductive t_ : Set := Le (z : Z) | Eq.
  Derive NoConfusion EqDec for t_.

  Definition t := t_.
  Definition eq : t t Prop := eq.
  Definition eq_equiv : Equivalence eq := _.

  Definition Le0 := Le 0.
  Definition Lt := Le 1.

  Inductive lt_ : t t Prop :=
  | LeLe n m : (n < m)%Z lt_ (Le n) (Le m)
  | LeEq n : lt_ (Le n) Eq.
  Derive Signature for lt_.
  Definition lt := lt_.

  Global Instance lt_strorder : StrictOrder lt.
  Proof.
    constructor.
    - intros []; intro X; inversion X. lia.
    - intros ? ? ? X Y; invs X; invs Y; constructor. lia.
  Qed.

  Global Instance lt_compat : Proper (eq ==> eq ==> iff) lt.
  Proof.
    intros ? ? X ? ? Y; invs X; invs Y. reflexivity.
  Qed.

  Definition compare (x y : t) : comparison :=
    match x, y with
    | Le n, Le mZ.compare n m
    | Le _, EqDatatypes.Lt
    | Eq, EqDatatypes.Eq
    | Eq, _Datatypes.Gt
    end.

  Lemma compare_spec x y : CompareSpec (eq x y) (lt x y) (lt y x) (compare x y).
  Proof.
    destruct x, y; repeat constructor. simpl.
    destruct (Z.compare_spec z z0); simpl; constructor.
    subst; constructor. now constructor. now constructor.
  Qed.

  Lemma eq_dec x y : {eq x y} + {¬ eq x y}.
  Proof.
    unfold eq. decide equality. apply Z.eq_dec.
  Qed.
End ConstraintType.

Module UnivConstraint.
  Definition t : Set := Level.t × ConstraintType.t × Level.t.

  Definition eq : t t Prop := eq.
  Definition eq_equiv : Equivalence eq := _.

  Definition make l1 ct l2 : t := (l1, ct, l2).

  Inductive lt_ : t t Prop :=
  | lt_Level2 l1 t l2 l2' : Level.lt l2 l2' lt_ (l1, t, l2) (l1, t, l2')
  | lt_Cstr l1 t t' l2 l2' : ConstraintType.lt t t' lt_ (l1, t, l2) (l1, t', l2')
  | lt_Level1 l1 l1' t t' l2 l2' : Level.lt l1 l1' lt_ (l1, t, l2) (l1', t', l2').
  Definition lt := lt_.

  Lemma lt_strorder : StrictOrder lt.
  Proof.
    constructor.
    - intros []; intro X; inversion X; subst;
        try (eapply Level.lt_strorder; eassumption).
      eapply ConstraintType.lt_strorder; eassumption.
    - intros ? ? ? X Y; invs X; invs Y; constructor; tea.
      etransitivity; eassumption.
      2: etransitivity; eassumption.
      eapply ConstraintType.lt_strorder; eassumption.
  Qed.

  Lemma lt_compat : Proper (eq ==> eq ==> iff) lt.
  Proof.
    intros ? ? X ? ? Y; invs X; invs Y. reflexivity.
  Qed.

  Definition compare : t t comparison :=
    fun '(l1, t, l2) '(l1', t', l2')
      compare_cont (Level.compare l1 l1')
        (compare_cont (ConstraintType.compare t t')
                    (Level.compare l2 l2')).

  Lemma compare_spec x y
    : CompareSpec (eq x y) (lt x y) (lt y x) (compare x y).
  Proof.
    destruct x as [[l1 t] l2], y as [[l1' t'] l2']; cbn.
    destruct (Level.compare_spec l1 l1'); cbn; repeat constructor; tas.
    invs H.
    destruct (ConstraintType.compare_spec t t'); cbn; repeat constructor; tas.
    invs H.
    destruct (Level.compare_spec l2 l2'); cbn; repeat constructor; tas.
    invs H. reflexivity.
  Qed.

  Lemma eq_dec x y : {eq x y} + {¬ eq x y}.
  Proof.
    unfold eq. decide equality; apply eq_dec.
  Defined.

  Definition eq_leibniz (x y : t) : eq x y x = y := id.
End UnivConstraint.

Module ConstraintSet := MSetAVL.Make UnivConstraint.
Module ConstraintSetFact := WFactsOn UnivConstraint ConstraintSet.
Module ConstraintSetProp := WPropertiesOn UnivConstraint ConstraintSet.
Module CS := ConstraintSet.
Module ConstraintSetDecide := WDecide (ConstraintSet).
Ltac csets := ConstraintSetDecide.fsetdec.

Notation "(=_cset)" := ConstraintSet.Equal (at level 0).
Infix "=_cset" := ConstraintSet.Equal (at level 30).

Definition declared_cstr_levels levels (cstr : UnivConstraint.t) :=
  let '(l1,_,l2) := cstr in
  LevelSet.In l1 levels LevelSet.In l2 levels.

Definition is_declared_cstr_levels levels (cstr : UnivConstraint.t) : bool :=
  let '(l1,_,l2) := cstr in
  LevelSet.mem l1 levels && LevelSet.mem l2 levels.

Lemma CS_union_empty s : ConstraintSet.union ConstraintSet.empty s =_cset s.
Proof.
  intros x; rewrite ConstraintSet.union_spec. lsets.
Qed.

Lemma CS_For_all_union f cst cst' : ConstraintSet.For_all f (ConstraintSet.union cst cst')
  ConstraintSet.For_all f cst.
Proof.
  unfold CS.For_all.
  intros IH x inx. apply (IH x).
  now eapply CS.union_spec; left.
Qed.

Lemma CS_For_all_add P x s : CS.For_all P (CS.add x s) P x CS.For_all P s.
Proof.
  intros.
  split.
  × apply (H x), CS.add_spec; left ⇒ //.
  × intros y iny. apply (H y), CS.add_spec; right ⇒ //.
Qed.

#[global] Instance CS_For_all_proper P : Morphisms.Proper ((=_cset) ==> iff)%signature (ConstraintSet.For_all P).
Proof.
  intros s s' eqs.
  unfold CS.For_all. split; intros IH x inxs; apply (IH x);
  now apply eqs.
Qed.

{6 Universe instances}

Module Instance.

A universe instance represents a vector of argument concreteUniverses to a polymorphic definition (constant, inductive or constructor).
  Definition t : Set := list Level.t.

  Definition empty : t := [].
  Definition is_empty (i : t) : bool :=
    match i with
    | []true
    | _false
    end.

  Definition eqb (i j : t) :=
    forallb2 Level.eqb i j.

  Definition equal_upto (f : Level.t Level.t bool) (i j : t) :=
    forallb2 f i j.
End Instance.

Module UContext.
  Definition t := list name × (Instance.t × ConstraintSet.t).

  Definition make' : Instance.t ConstraintSet.t Instance.t × ConstraintSet.t := pair.
  Definition make (ids : list name) (inst_ctrs : Instance.t × ConstraintSet.t) : t := (ids, inst_ctrs).

  Definition empty : t := ([], (Instance.empty, ConstraintSet.empty)).

  Definition instance : t Instance.t := fun xfst (snd x).
  Definition constraints : t ConstraintSet.t := fun xsnd (snd x).

  Definition dest : t list name × (Instance.t × ConstraintSet.t) := fun xx.
End UContext.

Module AUContext.
  Definition t := list name × ConstraintSet.t.

  Definition make (ids : list name) (ctrs : ConstraintSet.t) : t := (ids, ctrs).
  Definition repr (x : t) : UContext.t :=
    let (u, cst) := x in
    (u, (mapi (fun i _Level.Var i) u, cst)).

  Definition levels (uctx : t) : LevelSet.t :=
    LevelSetProp.of_list (fst (snd (repr uctx))).

  #[local]
  Existing Instance EqDec_ReflectEq.
  Definition inter (au av : AUContext.t) : AUContext.t :=
    let prefix := (split_prefix au.1 av.1).1.1 in
    let lvls := fold_left_i (fun s i _LevelSet.add (Level.Var i) s) prefix LevelSet.empty in
    let filter := ConstraintSet.filter (is_declared_cstr_levels lvls) in
    make prefix (ConstraintSet.union (filter au.2) (filter av.2)).
End AUContext.

Module ContextSet.
  Definition t := LevelSet.t × ConstraintSet.t.

  Definition levels : t LevelSet.t := fst.
  Definition constraints : t ConstraintSet.t := snd.

  Definition empty : t := (LevelSet.empty, ConstraintSet.empty).

  Definition is_empty (uctx : t)
    := LevelSet.is_empty (fst uctx) && ConstraintSet.is_empty (snd uctx).

  Definition equal (x y : t) : Prop :=
    x.1 =_lset y.1 x.2 =_cset y.2.

  Definition subset (x y : t) : Prop :=
    LevelSet.Subset (levels x) (levels y)
    ConstraintSet.Subset (constraints x) (constraints y).

  Definition inter (x y : t) : t :=
    (LevelSet.inter (levels x) (levels y),
      ConstraintSet.inter (constraints x) (constraints y)).

  Definition inter_spec (x y : t) :
    subset (inter x y) x
      subset (inter x y) y
       z, subset z x subset z y subset z (inter x y).
  Proof.
    split; last split.
    1,2: split⇒ ?; [move⇒ /LevelSet.inter_spec [//]|move⇒ /ConstraintSet.inter_spec [//]].
    move⇒ ? [??] [??]; split⇒ ??;
    [apply/LevelSet.inter_spec|apply/ConstraintSet.inter_spec]; split; auto.
  Qed.

  Definition union (x y : t) : t :=
    (LevelSet.union (levels x) (levels y), ConstraintSet.union (constraints x) (constraints y)).

  Definition union_spec (x y : t) :
    subset x (union x y)
      subset y (union x y)
       z, subset x z subset y z subset (union x y) z.
  Proof.
    split; last split.
    1,2: split⇒ ??; [apply/LevelSet.union_spec|apply/ConstraintSet.union_spec ]; by constructor.
    move⇒ ? [??] [??]; split⇒ ?;
    [move=>/LevelSet.union_spec|move=>/ConstraintSet.union_spec]=>-[]; auto.
  Qed.
End ContextSet.

Notation "(=_cs)" := ContextSet.equal (at level 0).
Notation "(⊂_cs)" := ContextSet.subset (at level 0).
Infix "=_cs" := ContextSet.equal (at level 30).
Infix "⊂_cs" := ContextSet.subset (at level 30).

Lemma incl_cs_refl cs : cs _cs cs.
Proof.
  split; [lsets|csets].
Qed.

Lemma empty_contextset_subset u : ContextSet.empty _cs u.
Proof.
  red. split; cbn; [lsets|csets].
Qed.

Module Variance.
A universe position in the instance given to a cumulative inductive can be the following. Note there is no Contravariant case because x : A, B x : A', B' requires A = A' as opposed to A' A.
  Inductive t :=
  | Irrelevant : t
  | Covariant : t
  | Invariant : t.
  Derive NoConfusion EqDec for t.

End Variance.

Inductive universes_decl : Type :=
| Monomorphic_ctx
| Polymorphic_ctx (cst : AUContext.t).

Derive NoConfusion for universes_decl.

Definition levels_of_udecl u :=
  match u with
  | Monomorphic_ctxLevelSet.empty
  | Polymorphic_ctx ctxAUContext.levels ctx
  end.

Definition constraints_of_udecl u :=
  match u with
  | Monomorphic_ctxConstraintSet.empty
  | Polymorphic_ctx ctxsnd (snd (AUContext.repr ctx))
  end.

Section Univ.
  Context {cf: checker_flags}.

  Inductive satisfies0 (v : valuation) : UnivConstraint.t Prop :=
  | satisfies0_Lt (l l' : Level.t) (z : Z) : (Z.of_nat (val v l) Z.of_nat (val v l') - z)%Z
                          satisfies0 v (l, ConstraintType.Le z, l')
  | satisfies0_Eq (l l' : Level.t) : val v l = val v l'
                          satisfies0 v (l, ConstraintType.Eq, l').

  Definition satisfies v : ConstraintSet.t Prop :=
    ConstraintSet.For_all (satisfies0 v).

  Lemma satisfies_union v φ1 φ2 :
    satisfies v (CS.union φ1 φ2)
     (satisfies v φ1 satisfies v φ2).
  Proof using Type.
    unfold satisfies. split.
    - intros H; split; intros c Hc; apply H; now apply CS.union_spec.
    - intros [H1 H2] c Hc; apply CS.union_spec in Hc; destruct Hc; auto.
  Qed.

  Lemma satisfies_subset φ φ' val :
    ConstraintSet.Subset φ φ'
    satisfies val φ'
    satisfies val φ.
  Proof using Type.
    intros sub sat ? isin.
    apply sat, sub; auto.
  Qed.

  Definition consistent ctrs := v, satisfies v ctrs.

  Definition consistent_extension_on cs cstr :=
     v, satisfies v (ContextSet.constraints cs) v',
        satisfies v' cstr
          LevelSet.For_all (fun lval v l = val v' l) (ContextSet.levels cs).

  Lemma consistent_extension_on_empty Σ :
    consistent_extension_on Σ CS.empty.
  Proof using Type.
    movev hv; v; split; [move⇒ ? /CS.empty_spec[]| move⇒ ??//].
  Qed.

  Lemma consistent_extension_on_union X cstrs
    (wfX : c, CS.In c X.2 LS.In c.1.1 X.1 LS.In c.2 X.1) :
    consistent_extension_on X cstrs
    consistent_extension_on X (CS.union cstrs X.2).
  Proof using Type.
    split.
    2: moveh v /h [v' [/satisfies_union [??] eqv']]; v'; split⇒ //.
    movehext v /[dup] vsat /hext [v' [v'sat v'eq]].
     v'; split⇒ //.
    apply/satisfies_union; split⇒ //.
    movec hc. destruct (wfX c hc).
    destruct (vsat c hc); constructor; rewrite -!v'eq //.
  Qed.

  Definition leq0_levelalg_n n φ (u u' : LevelAlgExpr.t) :=
     v, satisfies v φ (Z.of_nat (val v u) Z.of_nat (val v u') - n)%Z.

  Definition leq_levelalg_n n φ (u u' : LevelAlgExpr.t) :=
    if check_univs then leq0_levelalg_n n φ u u' else True.

  Definition leq_universe_n_ {CS} leq_levelalg_n n (φ: CS) s s' :=
    match s, s' with
    | Universe.lProp, Universe.lProp
    | Universe.lSProp, Universe.lSProp ⇒ (n = 0)%Z
    | Universe.lType u, Universe.lType u'leq_levelalg_n n φ u u'
    | Universe.lProp, Universe.lType uprop_sub_type
    | _, _False
    end.

  Definition leq_universe_n := leq_universe_n_ leq_levelalg_n.

  Definition leqb_universe_n_ leqb_levelalg_n b s s' :=
    match s, s' with
    | Universe.lProp, Universe.lProp
    | Universe.lSProp, Universe.lSPropnegb b
    | Universe.lType u, Universe.lType u'leqb_levelalg_n b u u'
    | Universe.lProp, Universe.lType uprop_sub_type
    | _, _false
    end.

  Definition eq0_levelalg φ (u u' : LevelAlgExpr.t) :=
     v, satisfies v φ val v u = val v u'.

  Definition eq_levelalg φ (u u' : LevelAlgExpr.t) :=
    if check_univs then eq0_levelalg φ u u' else True.

  Definition eq_universe_ {CS} eq_levelalg (φ: CS) s s' :=
    match s, s' with
    | Universe.lProp, Universe.lProp
    | Universe.lSProp, Universe.lSPropTrue
    | Universe.lType u, Universe.lType u'eq_levelalg φ u u'
    | _, _False
    end.

  Definition eq_universe := eq_universe_ eq_levelalg.

  Definition lt_levelalg := leq_levelalg_n 1.
  Definition leq_levelalg := leq_levelalg_n 0.
  Definition lt_universe := leq_universe_n 1.
  Definition leq_universe := leq_universe_n 0.

  Definition compare_universe (pb : conv_pb) :=
    match pb with
    | Conveq_universe
    | Cumulleq_universe
    end.

  Lemma leq_levelalg_leq_levelalg_n (φ : ConstraintSet.t) u u' :
    leq_levelalg φ u u' leq_levelalg_n 0 φ u u'.
  Proof using Type. intros. reflexivity. Qed.

  Lemma leq_universe_leq_universe_n (φ : ConstraintSet.t) u u' :
    leq_universe φ u u' leq_universe_n 0 φ u u'.
  Proof using Type. intros. reflexivity. Qed.


  Definition valid_constraints0 φ ctrs
    := v, satisfies v φ satisfies v ctrs.

  Definition valid_constraints φ ctrs
    := if check_univs then valid_constraints0 φ ctrs else True.

  Lemma valid_subset φ φ' ctrs
    : ConstraintSet.Subset φ φ' valid_constraints φ ctrs
       valid_constraints φ' ctrs.
  Proof using Type.
    unfold valid_constraints.
    destruct check_univs; [|trivial].
    intros Hφ H v Hv. apply H.
    intros ctr Hc. apply Hv. now apply Hφ.
  Qed.

  Lemma switch_minus (x y z : Z) : (x y - z x + z y)%Z.
  Proof using Type. split; lia. Qed.


Lemmas about eq and leq ****


  Ltac unfold_univ_rel0 :=
    unfold eq0_levelalg, leq0_levelalg_n in *;
    intros v Hv; cbnr.

  Ltac unfold_univ_rel :=
    unfold eq_levelalg, leq_levelalg, lt_levelalg, leq_levelalg_n in *;
    destruct check_univs; [unfold_univ_rel0 | trivial].

  Global Instance eq_levelalg_refl φ : Reflexive (eq_levelalg φ).
  Proof using Type.
    intros u; unfold_univ_rel.
  Qed.

  Global Instance eq_universe_refl φ : Reflexive (eq_universe φ).
  Proof using Type.
    intros []; cbnr.
  Qed.

  Global Instance leq_levelalg_n_refl φ : Reflexive (leq_levelalg φ).
  Proof using Type.
    intros u; unfold_univ_rel. lia.
  Qed.

  Global Instance leq_universe_refl φ : Reflexive (leq_universe φ).
  Proof using Type.
    intros []; cbnr.
  Qed.

  Global Instance eq_levelalg_sym φ : Symmetric (eq_levelalg φ).
  Proof using Type.
    intros u u' H; unfold_univ_rel.
    specialize (H v Hv); lia.
  Qed.

  Global Instance eq_universe_sym φ : Symmetric (eq_universe φ).
  Proof using Type.
    intros [] []; cbnr; auto.
    now symmetry.
  Qed.

  Global Instance eq_levelalg_trans φ : Transitive (eq_levelalg φ).
  Proof using Type.
    intros u u' u'' H1 H2; unfold_univ_rel.
    specialize (H1 v Hv); specialize (H2 v Hv); lia.
  Qed.

  Global Instance eq_universe_trans φ : Transitive (eq_universe φ).
  Proof using Type.
    intros [] [] []; cbnr; trivial; try absurd.
    etransitivity; eauto.
  Qed.

  Global Instance leq_levelalg_n_trans n φ : Transitive (leq_levelalg_n (Z.of_nat n) φ).
  Proof using Type.
    intros u u' u'' H1 H2; unfold_univ_rel.
    specialize (H1 v Hv); specialize (H2 v Hv); lia.
  Qed.

  Global Instance leq_universe_n_trans n φ : Transitive (leq_universe_n (Z.of_nat n) φ).
  Proof using Type.
    intros [] [] []; cbnr; trivial; try absurd.
    now etransitivity.
  Qed.

  Global Instance leq_universe_trans φ : Transitive (leq_universe φ).
  Proof using Type. apply (leq_universe_n_trans 0). Qed.

  Global Instance lt_universe_trans φ : Transitive (lt_universe φ).
  Proof using Type. apply (leq_universe_n_trans 1). Qed.

  Lemma eq0_leq0_levelalg φ u u' :
    eq0_levelalg φ u u' leq0_levelalg_n 0 φ u u' leq0_levelalg_n 0 φ u' u.
  Proof using Type.
    split.
    - intros H. split; unfold eq0_levelalg, leq_levelalg_n in *;
      intros v Hv; specialize (H v Hv); lia.
    - intros [H1 H2]. unfold eq0_levelalg, leq_levelalg_n in ×.
      intros v Hv. specialize (H1 v Hv); specialize (H2 v Hv); lia.
  Qed.

  Lemma eq_leq_levelalg φ u u' :
    eq_levelalg φ u u' leq_levelalg φ u u' leq_levelalg φ u' u.
  Proof using Type.
    split.
    - intros H. split; unfold_univ_rel; specialize (H v Hv); lia.
    - intros [H1 H2]. unfold_univ_rel. specialize (H1 v Hv); specialize (H2 v Hv); lia.
  Qed.

  Lemma eq_leq_universe φ u u' :
    eq_universe φ u u' leq_universe φ u u' leq_universe φ u' u.
  Proof using Type.
    destruct u, u'; cbnr; intuition auto.
    all: now apply eq_leq_levelalg.
  Qed.

  Lemma leq_levelalg_sup0_l φ u1 u2 : leq_levelalg φ u1 (LevelAlgExpr.sup u1 u2).
  Proof using Type. unfold_univ_rel. rewrite val_sup; lia. Qed.

  Lemma leq_levelalg_sup0_r φ u1 u2 : leq_levelalg φ u2 (LevelAlgExpr.sup u1 u2).
  Proof using Type. unfold_univ_rel. rewrite val_sup; lia. Qed.

  Lemma leq_levelalg_sup0_mon φ u1 u1' u2 u2' : leq_levelalg φ u1 u1' leq_levelalg φ u2 u2'
    leq_levelalg φ (LevelAlgExpr.sup u1 u2) (LevelAlgExpr.sup u1' u2').
  Proof using Type.
    intros H1 H2; unfold_univ_rel.
    specialize (H1 v Hv); specialize (H2 v Hv).
    rewrite !val_sup. lia.
  Qed.

  Lemma leq_universe_sup_l φ u1 s2 :
    let s1 := Universe.lType u1 in
    leq_universe φ s1 (Universe.sup s1 s2).
  Proof using Type.
    destruct s2 as [| | u2]; cbnr.
    apply leq_levelalg_sup0_l.
  Qed.

  Lemma leq_universe_sup_r φ s1 u2 :
    let s2 := Universe.lType u2 in
    leq_universe φ s2 (Universe.sup s1 s2).
  Proof using Type.
    destruct s1 as [| | u1]; cbnr.
    apply leq_levelalg_sup0_r.
  Qed.

  Lemma leq_universe_product φ (s1 s2 : Universe.t)
    : leq_universe φ s2 (Universe.sort_of_product s1 s2).
  Proof using Type.
    destruct s2 as [| | u2].
    - apply leq_universe_refl.
    - apply leq_universe_refl.
    - apply leq_universe_sup_r.
  Qed.

  Global Instance eq_universe_leq_universe φ : subrelation (eq_universe φ) (leq_universe φ).
  Proof using Type.
    intros u u'. apply eq_leq_universe.
  Qed.

  Global Instance eq_levelalg_equivalence φ : Equivalence (eq_levelalg φ) := Build_Equivalence _ _ _ _.

  Global Instance leq_levelalg_preorder φ : PreOrder (leq_levelalg φ) :=
    {| PreOrder_Transitive := leq_levelalg_n_trans 0 _ |}.

  Global Instance lt_levelalg_str_order {c: check_univs} φ (H: consistent φ) : StrictOrder (lt_levelalg φ).
  Proof using Type.
    split.
    - intros u; unfold complement, lt_levelalg, leq_levelalg_n; cbn.
      rewrite c; destruct H as [v Hv]; intros nH; specialize (nH v Hv); lia.
    - apply (leq_levelalg_n_trans 1).
  Qed.

  Global Instance leq_levelalg_antisym φ
    : Antisymmetric _ (eq_levelalg φ) (leq_levelalg φ).
  Proof using Type. intros t u tu ut. now apply eq_leq_levelalg. Qed.

  Global Instance leq_levelalg_partial_order φ
    : PartialOrder (eq_levelalg φ) (leq_levelalg φ).
  Proof.
    intros x y; split; apply eq_leq_levelalg.
  Defined.

  Global Instance eq_universe_equivalence φ : Equivalence (eq_universe φ) := Build_Equivalence _ _ _ _.

  Global Instance leq_universe_preorder φ : PreOrder (leq_universe φ) := Build_PreOrder _ _ _.

  Global Instance lt_universe_str_order {c: check_univs} φ (H: consistent φ) : StrictOrder (lt_universe φ).
  Proof using Type.
    split.
    - intros []; unfold complement; cbn; [lia|lia|].
      apply @StrictOrder_Irreflexive; apply @lt_levelalg_str_order; assumption.
    - exact _.
  Qed.

  Global Instance leq_universe_antisym φ
    : Antisymmetric _ (eq_universe φ) (leq_universe φ).
  Proof using Type. intros t u tu ut. now apply eq_leq_universe. Qed.

  Global Instance leq_universe_partial_order φ
    : PartialOrder (eq_universe φ) (leq_universe φ).
  Proof.
    intros x y; split; apply eq_leq_universe.
  Defined.

  Global Instance compare_universe_subrel pb Σ : subrelation (eq_universe Σ) (compare_universe pb Σ).
  Proof using Type.
    destruct pb; tc.
  Qed.

  Global Instance compare_universe_refl pb Σ : Reflexive (compare_universe pb Σ).
  Proof using Type.
    destruct pb; tc.
  Qed.

  Global Instance compare_universe_trans pb Σ : Transitive (compare_universe pb Σ).
  Proof using Type.
    destruct pb; tc.
  Qed.

  Global Instance compare_universe_preorder pb Σ : PreOrder (compare_universe pb Σ).
  Proof using Type.
    destruct pb; tc.
  Qed.

  Definition eq_universe_leq_universe' φ u u'
    := @eq_universe_leq_universe φ u u'.
  Definition leq_universe_refl' φ u
    := @leq_universe_refl φ u.

  Hint Resolve eq_universe_leq_universe' leq_universe_refl' : core.

  Lemma cmp_universe_subset ctrs ctrs' pb t u
    : ConstraintSet.Subset ctrs ctrs'
       compare_universe pb ctrs t u compare_universe pb ctrs' t u.
  Proof using Type.
    intros Hctrs.
    destruct pb, t, u; cbnr; trivial.
    all: intros H; unfold_univ_rel;
    apply H.
    all: eapply satisfies_subset; eauto.
  Qed.

  Lemma eq_universe_subset ctrs ctrs' t u
    : ConstraintSet.Subset ctrs ctrs'
       eq_universe ctrs t u eq_universe ctrs' t u.
  Proof using Type. apply cmp_universe_subset with (pb := Conv). Qed.

  Lemma leq_universe_subset ctrs ctrs' t u
    : ConstraintSet.Subset ctrs ctrs'
       leq_universe ctrs t u leq_universe ctrs' t u.
  Proof using Type. apply cmp_universe_subset with (pb := Cumul). Qed.

Elimination restriction

  Definition is_lSet φ s := eq_universe φ s Universe.type0.

  Definition is_allowed_elimination φ allowed : Universe.t Prop :=
    match allowed with
    | IntoSPropUniverse.is_sprop
    | IntoPropSPropis_propositional
    | IntoSetPropSPropfun sis_propositional s is_lSet φ s
    | IntoAnyfun sTrue
    end.

  Definition allowed_eliminations_subset (a a' : allowed_eliminations) : bool :=
    match a, a' with
    | IntoSProp, _
    | IntoPropSProp, (IntoPropSProp | IntoSetPropSProp | IntoAny)
    | IntoSetPropSProp, (IntoSetPropSProp | IntoAny)
    | IntoAny, IntoAnytrue
    | _, _false
    end.

  Lemma allowed_eliminations_subset_impl φ a a' s
    : allowed_eliminations_subset a a'
      is_allowed_elimination φ a s is_allowed_elimination φ a' s.
  Proof using Type.
    destruct a, a'; cbnr; trivial;
    destruct s; cbnr; trivial;
    intros H1 H2; try absurd; constructor; trivial.
  Qed.

End Univ.

Ltac unfold_univ_rel0 :=
  unfold eq0_levelalg, leq0_levelalg_n in *;
  intros v Hv; cbnr.

Ltac unfold_univ_rel :=
  unfold is_allowed_elimination, is_lSet,
  leq_universe, eq_universe, leq_universe_n, leq_universe_n_, eq_universe_,
  eq_levelalg, leq_levelalg, lt_levelalg, leq_levelalg_n in *;
  destruct check_univs; [unfold_univ_rel0 | trivial].

Tactic Notation "unfold_univ_rel" "eqn" ":"ident(H) :=
  unfold is_allowed_elimination, is_lSet,
  leq_universe, eq_universe, leq_universe_n, leq_universe_n_, eq_universe_,
  eq_levelalg, leq_levelalg, lt_levelalg, leq_levelalg_n in *;
  destruct check_univs eqn:H; [unfold_univ_rel0 | trivial].


Ltac cong := intuition congruence.

Lemma leq_universe_product_mon {cf} ϕ s1 s1' s2 s2' :
  leq_universe ϕ s1 s1'
  leq_universe ϕ s2 s2'
  leq_universe ϕ (Universe.sort_of_product s1 s2) (Universe.sort_of_product s1' s2').
Proof.
  destruct s2 as [| | u2], s2' as [| | u2']; cbnr; try absurd;
  destruct s1 as [| | u1], s1' as [| | u1']; cbnr; try absurd; trivial.
  - intros _ H2; etransitivity; [apply H2 | apply leq_levelalg_sup0_r].
  - apply leq_levelalg_sup0_mon.
Qed.

Lemma impredicative_product {cf} {ϕ l u} :
  Universe.is_prop u
  leq_universe ϕ (Universe.sort_of_product l u) u.
Proof.
  unfold Universe.sort_of_product.
  intros →. reflexivity.
Qed.

Section UniverseLemmas.
  Context {cf: checker_flags}.

  Lemma sup0_idem s : LevelAlgExpr.sup s s = s.
  Proof using Type.
    apply eq_univ'; cbn.
    intro; rewrite !LevelExprSet.union_spec. intuition.
  Qed.

  Lemma sup_idem s : Universe.sup s s = s.
  Proof using Type.
    destruct s; cbn; auto.
    apply f_equal.
    apply sup0_idem.
  Qed.

  Lemma sort_of_product_idem s
    : Universe.sort_of_product s s = s.
  Proof using Type.
    unfold Universe.sort_of_product; destruct s; try reflexivity.
    apply sup_idem.
  Qed.

  Lemma sup0_assoc s1 s2 s3 :
    LevelAlgExpr.sup s1 (LevelAlgExpr.sup s2 s3) = LevelAlgExpr.sup (LevelAlgExpr.sup s1 s2) s3.
  Proof using Type.
    apply eq_univ'; cbn. symmetry; apply LevelExprSetProp.union_assoc.
  Qed.

  Instance proper_sup0_eq_levelalg φ :
    Proper (eq_levelalg φ ==> eq_levelalg φ ==> eq_levelalg φ) LevelAlgExpr.sup.
  Proof using Type.
    intros u1 u1' H1 u2 u2' H2.
    unfold_univ_rel.
    specialize (H1 v Hv); specialize (H2 v Hv).
    rewrite !val_sup. lia.
  Qed.

  Instance universe_sup_eq_universe φ :
    Proper (eq_universe φ ==> eq_universe φ ==> eq_universe φ) Universe.sup.
  Proof using Type.
    intros [| | u1] [| |u1'] H1 [| |u2] [| |u2'] H2; cbn in *; try absurd; auto.
    now apply proper_sup0_eq_levelalg.
  Qed.

  Lemma sort_of_product_twice u s :
    Universe.sort_of_product u (Universe.sort_of_product u s)
    = Universe.sort_of_product u s.
  Proof using Type.
    destruct u,s; cbnr.
    now rewrite sup0_assoc sup0_idem.
  Qed.
End UniverseLemmas.

Section no_prop_leq_type.
  Context {cf: checker_flags}.
  Context (ϕ : ConstraintSet.t).

  Lemma succ_inj x y : LevelExpr.succ x = LevelExpr.succ y x = y.
  Proof using Type.
    unfold LevelExpr.succ.
    destruct x as [l n], y as [l' n']. simpl. congruence.
  Qed.

  Lemma spec_map_succ l x :
    LevelExprSet.In x (LevelAlgExpr.succ l)
     x', LevelExprSet.In x' l x = LevelExpr.succ x'.
  Proof using Type.
    rewrite map_spec. reflexivity.
  Qed.

  Lemma val_succ v l : val v (LevelExpr.succ l) = val v l + 1.
  Proof using Type.
    destruct l as []; simpl. cbn. lia.
  Qed.

  Lemma val_map_succ v l : val v (LevelAlgExpr.succ l) = val v l + 1.
  Proof using Type.
    pose proof (spec_map_succ l).
    set (n := LevelAlgExpr.succ l) in ×.
    destruct (val_In_max l v) as [max [inmax eqv]]. rewrite <-eqv.
    rewrite val_caract. split.
    intros.
    specialize (proj1 (H _) H0) as [x' [inx' eq]]. subst e.
    rewrite val_succ. eapply (val_In_le _ v) in inx'. rewrite <- eqv in inx'.
    simpl in ×. unfold LevelExprSet.elt, LevelExpr.t in ×. lia.
     (LevelExpr.succ max). split. apply H.
     max; split; auto.
    now rewrite val_succ.
  Qed.

  Lemma leq_universe_super s s' :
    leq_universe ϕ s s'
    leq_universe ϕ (Universe.super s) (Universe.super s').
  Proof using Type.
    destruct s as [| | u1], s' as [| | u1']; cbnr; try absurd;
    intros H; unfold_univ_rel;
    rewrite !val_map_succ. lia.
    specialize (H v Hv). lia.
  Qed.

  Lemma leq_universe_props s1 s2 :
    check_univs
    consistent ϕ
    leq_universe ϕ s1 s2
    match s1, s2 with
    | Universe.lProp, Universe.lPropTrue
    | Universe.lSProp, Universe.lSPropTrue
    | Universe.lProp, Universe.lSPropFalse
    | Universe.lSProp, Universe.lPropFalse
    | Universe.lProp, Universe.lType _prop_sub_type
    | Universe.lSProp, Universe.lType _False
    | Universe.lType l, Universe.lType l'True
    | Universe.lType _, _False
    end.
  Proof using Type.
    destruct s1, s2; cbnr; trivial.
  Qed.

  Lemma leq_universe_prop_r s1 s2 :
    check_univs
    consistent ϕ
    leq_universe ϕ s1 s2
    Universe.is_prop s2 Universe.is_prop s1.
  Proof using Type.
    intros Hcf cu.
    destruct s2; cbn; [ | absurd | absurd].
    destruct s1; cbn; [ auto | absurd | absurd].
  Qed.

  Lemma leq_universe_sprop_r s1 s2 :
    check_univs
    consistent ϕ
    leq_universe ϕ s1 s2
    Universe.is_sprop s2 Universe.is_sprop s1.
  Proof using Type.
    intros Hcf cu.
    destruct s2; cbn; [ absurd | | absurd].
    destruct s1; cbn; [ absurd | auto | absurd].
  Qed.

  Lemma leq_universe_prop_no_prop_sub_type s1 s2 :
    check_univs
    prop_sub_type = false
    consistent ϕ
    leq_universe ϕ s1 s2
    Universe.is_prop s1 Universe.is_prop s2.
  Proof using Type.
    intros Hcf ps cu.
    destruct s1; cbn; [ | absurd | absurd].
    rewrite ps.
    destruct s2; cbn; [ auto | absurd | absurd].
  Qed.

  Lemma leq_universe_sprop_l s1 s2 :
    check_univs
    consistent ϕ
    leq_universe ϕ s1 s2
    Universe.is_sprop s1 Universe.is_sprop s2.
  Proof using Type.
    intros Hcf cu.
    destruct s1; cbn; [ absurd | | absurd].
    destruct s2; cbn; [ absurd | auto | absurd].
  Qed.

  Hint Resolve leq_universe_sprop_l leq_universe_sprop_r
        leq_universe_prop_r
        leq_universe_prop_no_prop_sub_type
        : univ_lemmas.

  Lemma leq_prop_prop {l l'} :
    Universe.is_prop l Universe.is_prop l'
    leq_universe ϕ l l'.
  Proof using Type.
    destruct l, l'; cbnr; absurd.
  Qed.

  Lemma leq_sprop_sprop {l l'} :
    Universe.is_sprop l Universe.is_sprop l'
    leq_universe ϕ l l'.
  Proof using Type.
    destruct l, l'; cbnr; absurd.
  Qed.

  Lemma leq_prop_is_prop_sprop {s1 s2} :
    check_univs
    prop_sub_type = false
    consistent ϕ
    leq_universe ϕ s1 s2
    is_propositional s1 is_propositional s2.
  Proof using Type.
    intros Hcf ps cu.
    destruct s1, s2; cbn; try absurd; intros H; split; trivial.
    now rewrite ps in H.
  Qed.

  Lemma is_prop_gt s1 s2 :
    check_univs
    consistent ϕ
    leq_universe ϕ (Universe.super s1) s2 Universe.is_prop s2 False.
  Proof using Type.
    intros Hcf cu Hleq Hprop.
    apply leq_universe_prop_r in Hleq; tas.
    now destruct s1.
  Qed.

  Lemma is_sprop_gt s1 s2 :
    check_univs
    consistent ϕ
    leq_universe ϕ (Universe.super s1) s2 Universe.is_sprop s2 False.
  Proof using Type.
    intros Hcf cu Hleq Hprop.
    apply leq_universe_sprop_r in Hleq; tas.
    now destruct s1.
  Qed.

End no_prop_leq_type.

Definition fresh_level : Level.t := Level.Level "__metacoq_fresh_level__".
Definition fresh_universe : Universe.t := Universe.make fresh_level.

Universe substitution

Substitution of universe levels for universe level variables, used to implement universe polymorphism.
Substitutable type

Class UnivSubst A := subst_instance : Instance.t A A.

Notation "x @[ u ]" := (subst_instance u x) (at level 3,
  format "x @[ u ]").

#[global] Instance subst_instance_level : UnivSubst Level.t :=
  fun u lmatch l with
            Level.lzero | Level.Level _l
          | Level.Var nList.nth n u Level.lzero
          end.

#[global] Instance subst_instance_cstr : UnivSubst UnivConstraint.t :=
  fun u c(subst_instance_level u c.1.1, c.1.2, subst_instance_level u c.2).

#[global] Instance subst_instance_cstrs : UnivSubst ConstraintSet.t :=
  fun u ctrsConstraintSet.fold (fun cConstraintSet.add (subst_instance_cstr u c))
                                ctrs ConstraintSet.empty.

#[global] Instance subst_instance_level_expr : UnivSubst LevelExpr.t :=
  fun u ematch e with
          | (Level.lzero, _)
          | (Level.Level _, _)e
          | (Level.Var n, b)
            match nth_error u n with
            | Some l(l,b)
            | None(Level.lzero, b)
            end
          end.

#[global] Instance subst_instance_univ0 : UnivSubst LevelAlgExpr.t :=
  fun umap (subst_instance_level_expr u).

#[global] Instance subst_instance_univ : UnivSubst Universe.t :=
  fun u ematch e with
          | Universe.lProp | Universe.lSPrope
          | Universe.lType lUniverse.lType (subst_instance u l)
          end.

#[global] Instance subst_instance_instance : UnivSubst Instance.t :=
  fun u u'List.map (subst_instance_level u) u'.

Tests that the term is closed over k universe variables
Section Closedu.
  Context (k : nat).

  Definition closedu_level (l : Level.t) :=
    match l with
    | Level.Var n ⇒ (n <? k)%nat
    | _true
    end.

  Definition closedu_level_expr (s : LevelExpr.t) :=
    closedu_level (LevelExpr.get_level s).

  Definition closedu_universe_levels (u : LevelAlgExpr.t) :=
    LevelExprSet.for_all closedu_level_expr u.

  Definition closedu_universe (u : Universe.t) :=
    match u with
    | Universe.lSProp | Universe.lProptrue
    | Universe.lType lclosedu_universe_levels l
    end.

  Definition closedu_instance (u : Instance.t) :=
    forallb closedu_level u.
End Closedu.

Universe-closed terms are unaffected by universe substitution.
Section UniverseClosedSubst.
  Lemma closedu_subst_instance_level u l
  : closedu_level 0 l subst_instance_level u l = l.
  Proof.
    destruct l; cbnr. discriminate.
  Qed.

  Lemma closedu_subst_instance_level_expr u e
    : closedu_level_expr 0 e subst_instance_level_expr u e = e.
  Proof.
    intros.
    destruct e as [t b]. destruct t;cbnr. discriminate.
  Qed.

  Lemma closedu_subst_instance_univ u s
    : closedu_universe 0 s subst_instance_univ u s = s.
  Proof.
    intro H.
    destruct s as [| | t]; cbnr.
    apply f_equal. apply eq_univ'.
    destruct t as [ts H1].
    unfold closedu_universe_levels in *;cbn in ×.
    intro e; split; intro He.
    - apply map_spec in He. destruct He as [e' [He' X]].
      rewrite closedu_subst_instance_level_expr in X.
      apply LevelExprSet.for_all_spec in H; proper.
      exact (H _ He').
      now subst.
    - apply map_spec. e; split; tas.
      symmetry; apply closedu_subst_instance_level_expr.
      apply LevelExprSet.for_all_spec in H; proper. now apply H.
  Qed.

  Lemma closedu_subst_instance u t
    : closedu_instance 0 t subst_instance u t = t.
  Proof.
    intro H. apply forall_map_id_spec.
    apply Forall_forall; intros l Hl.
    apply closedu_subst_instance_level.
    eapply forallb_forall in H; eassumption.
  Qed.

End UniverseClosedSubst.

#[global]
Hint Resolve closedu_subst_instance_level closedu_subst_instance_level_expr
     closedu_subst_instance_univ closedu_subst_instance : substu.

Substitution of a universe-closed instance of the right size produces a universe-closed term.
Section SubstInstanceClosed.
  Context (u : Instance.t) (Hcl : closedu_instance 0 u).

  Lemma subst_instance_level_closedu l
    : closedu_level #|u| l closedu_level 0 (subst_instance_level u l).
  Proof using Hcl.
    destruct l; cbnr.
    unfold closedu_instance in Hcl.
    destruct (nth_in_or_default n u Level.lzero).
    - intros _. eapply forallb_forall in Hcl; tea.
    - rewrite e; reflexivity.
  Qed.

  Lemma subst_instance_level_expr_closedu e :
    closedu_level_expr #|u| e closedu_level_expr 0 (subst_instance_level_expr u e).
  Proof using Hcl.
    destruct e as [l b]. destruct l;cbnr.
    case_eq (nth_error u n); cbnr. intros [] Hl X; cbnr.
    apply nth_error_In in Hl.
    eapply forallb_forall in Hcl; tea.
    discriminate.
  Qed.

  Lemma subst_instance_univ_closedu s
    : closedu_universe #|u| s closedu_universe 0 (subst_instance_univ u s).
  Proof using Hcl.
    intro H.
    destruct s as [| |t]; cbnr.
    destruct t as [l Hl].
    apply LevelExprSet.for_all_spec; proper.
    intros e He. eapply map_spec in He.
    destruct He as [e' [He' X]]; subst.
    apply subst_instance_level_expr_closedu.
    apply LevelExprSet.for_all_spec in H; proper.
    now apply H.
  Qed.

  Lemma subst_instance_closedu t :
    closedu_instance #|u| t closedu_instance 0 (subst_instance u t).
  Proof using Hcl.
    intro H. etransitivity. eapply forallb_map.
    eapply forallb_impl; tea.
    intros l Hl; cbn. apply subst_instance_level_closedu.
  Qed.
End SubstInstanceClosed.

#[global]
Hint Resolve subst_instance_level_closedu subst_instance_level_expr_closedu
     subst_instance_univ_closedu subst_instance_closedu : substu.

Definition string_of_level (l : Level.t) : string :=
  match l with
  | Level.lzero ⇒ "Set"
  | Level.Level ss
  | Level.Var n ⇒ "Var" ^ string_of_nat n
  end.

Definition string_of_level_expr (e : LevelExpr.t) : string :=
  let '(l, n) := e in string_of_level l ^ (if n is 0 then "" else "+" ^ string_of_nat n).

Definition string_of_sort (u : Universe.t) :=
  match u with
  | Universe.lSProp ⇒ "SProp"
  | Universe.lProp ⇒ "Prop"
  | Universe.lType l ⇒ "Type(" ^ string_of_list string_of_level_expr (LevelExprSet.elements l) ^ ")"
  end.

Definition string_of_universe_instance u :=
  string_of_list string_of_level u.

Inductive universes_entry :=
| Monomorphic_entry (ctx : ContextSet.t)
| Polymorphic_entry (ctx : UContext.t).
Derive NoConfusion for universes_entry.

Definition universes_entry_of_decl (u : universes_decl) : universes_entry :=
  match u with
  | Polymorphic_ctx ctxPolymorphic_entry (Universes.AUContext.repr ctx)
  | Monomorphic_ctxMonomorphic_entry ContextSet.empty
  end.

Definition polymorphic_instance uctx :=
  match uctx with
  | Monomorphic_ctxInstance.empty
  | Polymorphic_ctx cfst (snd (AUContext.repr c))
  end.
Definition abstract_instance decl :=
  match decl with
  | Monomorphic_ctxInstance.empty
  | Polymorphic_ctx auctxUContext.instance (AUContext.repr auctx)
  end.

Definition print_universe_instance u :=
  match u with
  | [] ⇒ ""
  | _ ⇒ "@{" ^ print_list string_of_level " " u ^ "}"
  end.

Definition print_lset t :=
  print_list string_of_level " " (LevelSet.elements t).

Definition print_constraint_type d :=
  match d with
  | ConstraintType.Le n
    if (n =? 0)%Z then "<=" else
    if (n =? 1)%Z then "<" else
    if (n <? 0)%Z then "<=" ^ string_of_nat (Z.to_nat (Z.abs n)) ^ " + "
    else " + " ^ string_of_nat (Z.to_nat n) ^ " <= "
  | ConstraintType.Eq ⇒ "="
  end.

Definition print_constraint_set t :=
  print_list (fun '(l1, d, l2)string_of_level l1 ^ " " ^
                         print_constraint_type d ^ " " ^ string_of_level l2)
             " /\ " (ConstraintSet.elements t).