(* Some prelude to be able to test the file without being able to compile the prelude *)

Notation "A -> B" := (forall (_ : A), B) (right associativity, at level 99).

Inductive nat : Type :=
| zero : nat
| S : nat -> nat.

Inductive bool : Type :=
| true : bool
| false : bool.

(* The type unit needs to be defined and registered to instantiate partial nesting. *)
Inductive unit : Set :=
    tt : unit.

Register unit as core.unit.type.
Register tt as core.unit.tt.

Set Printing Universes.

Module Template.

  (* Example with Template Inductive Types *)

  Set Depth Scheme All 2.

  Inductive True := c.

  Fail Check True_all.

  (* Example with list *)
  Inductive list (A : Type) : Type :=
  | nil : list A
  | cons : A -> list A -> list A.

  Print list_all.
  About list_all_forall.
  Print list_all_all.
  About list_all_all_forall.

  Arguments nil {_}.
  Arguments cons {_}.

  Inductive MRT : Set :=
  | MRTnode : list MRT -> MRT.

  About MRT_ind.

  Inductive RoseTree A : Type :=
  | RTleaf (a : A) : RoseTree A
  | RTnode (l : list (RoseTree A)) : RoseTree A.

  About RoseTree_ind.
  Print RoseTree_all.
  About RoseTree_all_forall.

  Inductive RoseTreeMut A :=
  | node_mut : forest A -> RoseTreeMut A
  with forest A :=
  | forest_nil : forest A
  | forest_cons : A -> RoseTreeMut A -> forest A.

  Print RoseTreeMut_all.
  About RoseTreeMut_all_forall.
  Print RoseTreeMut_all_all.
  About RoseTreeMut_all_all_forall.

  Print forest_all.
  About forest_all_forall.
  Print forest_all_all.
  About forest_all_all_forall.

  Inductive RoseRoseTree A : Type :=
  | Nleaf (a : A) : RoseRoseTree A
  | Nnode (p : (list (list (RoseRoseTree A)))) : RoseRoseTree A.

  About RoseRoseTree_ind.
  Print RoseRoseTree_all.
  About RoseRoseTree_all_forall.

  Inductive ArrowTree1 A : Type :=
  | ATleaf1 (a : A) : ArrowTree1 A
  | ATnode1 (l : (bool -> list (ArrowTree1 A))) : ArrowTree1 A.

  About ArrowTree1_ind.
  Print ArrowTree1_all.
  About ArrowTree1_all_forall.

  Inductive ArrowTree2 A : Type :=
  | ATleaf2 (a : A) : ArrowTree2 A
  | ATnode2 (l : list (nat -> ArrowTree2 A)) : ArrowTree2 A.

  About ArrowTree2_ind.
  Print ArrowTree2_all.
  About ArrowTree2_all_forall.

  Inductive ArrowTree3 A : Type :=
  | ATleaf3 (a : A) : ArrowTree3 A
  | ATnode3 (l : (bool -> list (nat -> ArrowTree3 A))) : ArrowTree3 A.

  About ArrowTree3_ind.
  Print ArrowTree3_all.
  About ArrowTree3_all_forall.


  (* Example Prod *)
  Inductive prod (A B : Type) : Type :=
    pair : A -> B -> prod A B.

  Print prod_all.
  About prod_all_forall.
  Print prod_all_all.
  About prod_all_all_forall.

  Arguments pair {_ _}.

  Inductive PairTree A : Type :=
  | Pleaf (a : A) : PairTree A
  | Pnode (p : prod (PairTree A) (PairTree A)) : PairTree A.

  About PairTree_ind.
  Print PairTree_all.
  About PairTree_all_forall.

  Module AutoGeneratedScheme.

    Inductive LeftTree A : Type :=
    | Lleaf (a : A) : LeftTree A
    | Lnode (p : prod (LeftTree A) nat) : LeftTree A.

    About LeftTree_ind.
    Print LeftTree_all.
    About LeftTree_all_forall.

  End AutoGeneratedScheme.

  Module Scheme.

    Unset Elimination Schemes.

    Inductive LeftTree A : Type :=
    | Lleaf (a : A) : LeftTree A
    | Lnode (p : prod (LeftTree A) nat) : LeftTree A.

    Scheme Induction for LeftTree Sort Prop.

    About LeftTree_ind.
    Print LeftTree_all.
    About LeftTree_all_forall.

  End Scheme.

  Module PartialAll.

    Scheme All for prod over A.
    Print prod_all_10.
    About prod_all_forall_10.
    Print prod_all_10_all.
    About prod_all_10_all_forall.

    Inductive LeftTree A : Type :=
    | Lleaf (a : A) : LeftTree A
    | Lnode (p : prod (LeftTree A) nat) : LeftTree A.

    About LeftTree_ind.
    Print LeftTree_all.
    About LeftTree_all_forall.

  End PartialAll.

  Inductive RightTree A : Type :=
  | Rleaf (a : A) : RightTree A
  | Rnode (p : prod nat (RightTree A)) : RightTree A.

  About RightTree_ind.
  Print RightTree_all.
  About RightTree_all_forall.


  (* Nesting with vec *)
  Inductive vec A : nat -> Type :=
  | vnil : vec A zero
  | vcons : A -> forall n, vec A n -> vec A (S n).

  Print vec_all.
  About vec_all_forall.
  Print vec_all_all.
  About vec_all_all_forall.

  Inductive VecTree A : Type :=
  | VNleaf (a : A) : VecTree A
  | VNnode n (p : vec (VecTree A) n) : VecTree A.

  About VecTree_ind.
  Print VecTree_all.
  About VecTree_all_forall.



  (* Example All2i *)
  Inductive All2i (A B : Type) (R : nat -> A -> B -> Type) (n : nat) : list A -> list B -> Type :=
    | All2i_nil : All2i A B R n nil nil
    | All2i_cons : forall (a : A) (b : B) (lA : list A) (lB : list B),
                  R n a b -> All2i A B R (S n) lA lB -> All2i A B R n (cons a lA) (cons b lB).

  Print All2i_all.
  About All2i_all_forall.
  Print All2i_all_all.
  About All2i_all_all_forall.

  Fail Scheme All for All2i over A.

  Inductive typing A B (n : nat) (a : A) (b : B) : Type :=
  | typ_nil  : typing A B n a b
  | typ_cons : forall (lA : list A) (lB : list B),
              All2i A B (fun n => typing A B n) n lA lB -> typing A B n a b.

  About typing_ind.


  (* Example All2i_bis_bis with trivial nesting on R *)
  Inductive All2i_bis (A B C : Type) (R : nat -> A -> B -> Type) (n : nat) : list A -> list B -> Type :=
    | All2i_bis_nil : C -> All2i_bis A B C R n nil nil
    | All2i_bis_cons : forall (a : A) (b : B) (lA : list A) (lB : list B),
                  R n a b -> All2i_bis A B C R (S n) lA lB -> All2i_bis A B C R n (cons a lA) (cons b lB).

  Print All2i_bis_all.
  About All2i_bis_all_forall.
  Print All2i_bis_all_all.
  About All2i_bis_all_all_forall.

  Inductive triv_All2_bis : Type :=
  | ctriv_All2_bis : All2i_bis bool bool triv_All2_bis (fun _ _ _ => nat) zero (@nil bool) (@nil bool) ->
                    triv_All2_bis.

  About triv_All2_bis_ind.


  (* sig *)
  Inductive sig (A : Type) (P : A -> Prop) : Type :=
  | exist : forall x : A, P x -> sig A P.

  Print sig_all.
  About sig_all_forall.

  Scheme All for sig over A.
  Print sig_all_10.
  About sig_all_forall_10.

  Scheme All for sig over P.
  Print sig_all_01.
  About sig_all_forall_01.


  (* Test collapse above prop *)
  Inductive and (A B : Prop) : Prop :=
  | conj : A -> B -> and A B.

  Inductive or (A B : Prop) : Prop :=
  | or_introl : A -> or A B
  | or_intror : B -> or A B.

  Inductive NestRel A (R : A -> A -> Prop) :=
  | mkR : forall x, or (and (R x x) (R x x)) True -> NestRel A R.

  Print NestRel_all.
  About NestRel_all_forall.


  (* Test Primtive Projections *)
  Module PrimRecord.

    Set Primitive Projections.

    Inductive X (U:Type) := { b : U }.

    Fail Scheme All for X.

  End PrimRecord.


  (* Test Deeply Nested + variable binding *)
  Inductive ex (A:Type) (P:A -> Prop) : Prop :=
  ex_intro : forall x:A, P x -> ex A P.

  Print ex_all.
  About ex_all_forall.

  Record language := Language {
    expr : Type;
    prim_step : expr -> Prop;
  }.

  Inductive adequate L (φ : unit -> Type) :=
  | cadequate : prod False (ex (expr L) (fun e2 => prim_step L e2)) -> adequate L φ.

  About adequate_ind.
  About adequate_all.

  (* ISSUE 21710 *)
  Inductive casenat (P : nat -> Set) : Set :=
  | casenat_fold : (forall m, P m) -> casenat P.

  About casenat_all.

  Inductive casenat' (A : nat -> Set) (PZ : Set) (PS : forall n, A n -> Set) : Set :=
  | casenat'_fold : (forall m a, PS m a) -> casenat' A PZ PS.

  About casenat'_all.

End Template.


Module UnivPoly.

  (* Example with Template Inductive Types *)

  Set Universe Polymorphism.

  Set Depth Scheme All 2.

  Inductive True := c.

  Fail Check True_all.

  (* Example with list *)
  Inductive list (A : Type) : Type :=
  | nil : list A
  | cons : A -> list A -> list A.

  Print list_all.
  About list_all_forall.
  Print list_all_all.
  About list_all_all_forall.

  Inductive MRT : Set :=
  | MRTnode : list MRT -> MRT.

  About MRT_ind.

  Inductive RoseTree A : Type :=
  | RTleaf (a : A) : RoseTree A
  | RTnode (l : list (RoseTree A)) : RoseTree A.

  About RoseTree_ind.
  Print RoseTree_all.
  About RoseTree_all_forall.

  Inductive RoseRoseTree A : Type :=
  | Nleaf (a : A) : RoseRoseTree A
  | Nnode (p : (list (list (RoseRoseTree A)))) : RoseRoseTree A.

  About RoseRoseTree_ind.
  Print RoseRoseTree_all.
  About RoseRoseTree_all_forall.

  Inductive ArrowTree3 A : Type :=
  | ATleaf3 (a : A) : ArrowTree3 A
  | ATnode3 (l : (bool -> list (nat -> ArrowTree3 A))) : ArrowTree3 A.

  About ArrowTree3_ind.
  Print ArrowTree3_all.
  About ArrowTree3_all_forall.

End UnivPoly.


Module SortPoly.

  (* Example with a sort polymorphic containers *)
  Set Universe Polymorphism.

  Inductive list@{sA;uA uR} (A : Type@{sA;uA}) : Type@{sA; uR} :=
  | nil : list A
  | cons : A -> list A -> list A.

  Scheme All for list.
  Print list_all.
  About list_all_forall.

  Scheme All for list_all.
  Print list_all_all.
  About list_all_all_forall.

  Inductive MRT : Set :=
  | MRTnode : list MRT -> MRT.

  About MRT_ind.

  Inductive SRT : SProp :=
  | SRTnode : list SRT -> SRT.

  About SRT_sind.

End SortPoly.



Module TestWarning.

  Set Warnings "+register-all".

  Inductive list (A : Type) : Type :=
  | nil : list A
  | cons : A -> list A -> list A.

  Fail Inductive MRT : Set :=
  | MRTnode : list MRT -> MRT.

  Set Warnings "-register-all".

  Inductive MRT : Set :=
  | MRTnode : list MRT -> MRT.

  About MRT_ind.

End TestWarning.



Module DeepArities.
  Definition P A := A -> Type.

  Inductive Nester (X : unit -> P unit) : Type :=
  | nester_intro : X tt tt -> Nester X.
  Scheme All for Nester.

  About Nester_all.
End DeepArities.
