Library MetaCoq.PCUIC.PCUICAstUtils





Fixpoint string_of_term (t : term) :=
  match t with
  | tRel n ⇒ "Rel(" ++ string_of_nat n ++ ")"
  | tVar n ⇒ "Var(" ++ n ++ ")"
  | tEvar ev args ⇒ "Evar(" ++ string_of_nat ev ++ "," ++ string_of_list string_of_term args ++ ")"
  | tSort s ⇒ "Sort(" ++ string_of_sort s ++ ")"
  | tProd na b t ⇒ "Prod(" ++ string_of_name na ++ "," ++
                            string_of_term b ++ "," ++ string_of_term t ++ ")"
  | tLambda na b t ⇒ "Lambda(" ++ string_of_name na ++ "," ++ string_of_term b
                                ++ "," ++ string_of_term t ++ ")"
  | tLetIn na b t' t ⇒ "LetIn(" ++ string_of_name na ++ "," ++ string_of_term b
                                 ++ "," ++ string_of_term t' ++ "," ++ string_of_term t ++ ")"
  | tApp f l ⇒ "App(" ++ string_of_term f ++ "," ++ string_of_term l ++ ")"
  | tConst c u ⇒ "Const(" ++ c ++ "," ++ string_of_universe_instance u ++ ")"
  | tInd i u ⇒ "Ind(" ++ string_of_inductive i ++ "," ++ string_of_universe_instance u ++ ")"
  | tConstruct i n u ⇒ "Construct(" ++ string_of_inductive i ++ "," ++ string_of_nat n ++ ","
                                    ++ string_of_universe_instance u ++ ")"
  | tCase (ind, i) t p brs
    "Case(" ++ string_of_inductive ind ++ "," ++ string_of_nat i ++ "," ++ string_of_term t ++ ","
            ++ string_of_term p ++ "," ++ string_of_list (fun bstring_of_term (snd b)) brs ++ ")"
  | tProj (ind, i, k) c
    "Proj(" ++ string_of_inductive ind ++ "," ++ string_of_nat i ++ "," ++ string_of_nat k ++ ","
            ++ string_of_term c ++ ")"
  | tFix l n ⇒ "Fix(" ++ (string_of_list (string_of_def string_of_term) l) ++ "," ++ string_of_nat n ++ ")"
  | tCoFix l n ⇒ "CoFix(" ++ (string_of_list (string_of_def string_of_term) l) ++ "," ++ string_of_nat n ++ ")"
  end.

Make a lambda/let-in string of abstractions from a context Γ, ending with term t.

Definition mkLambda_or_LetIn d t :=
  match d.(decl_body) with
  | NonetLambda d.(decl_name) d.(decl_type) t
  | Some btLetIn d.(decl_name) b d.(decl_type) t
  end.

Definition it_mkLambda_or_LetIn (l : context) (t : term) :=
  List.fold_left (fun acc dmkLambda_or_LetIn d acc) l t.

Make a prod/let-in string of abstractions from a context Γ, ending with term t.

Definition mkProd_or_LetIn d t :=
  match d.(decl_body) with
  | NonetProd d.(decl_name) d.(decl_type) t
  | Some btLetIn d.(decl_name) b d.(decl_type) t
  end.

Definition it_mkProd_or_LetIn (l : context) (t : term) :=
  List.fold_left (fun acc dmkProd_or_LetIn d acc) l t.

Definition map_decl f (d : context_decl) :=
  {| decl_name := d.(decl_name);
     decl_body := option_map f d.(decl_body);
     decl_type := f d.(decl_type) |}.

Lemma map_decl_type f decl : f (decl_type decl) = decl_type (map_decl f decl).

Lemma map_decl_body f decl : option_map f (decl_body decl) = decl_body (map_decl f decl).

Lemma option_map_decl_body_map_decl f x :
  option_map decl_body (option_map (map_decl f) x) =
  option_map (option_map f) (option_map decl_body x).

Lemma option_map_decl_type_map_decl f x :
  option_map decl_type (option_map (map_decl f) x) =
  option_map f (option_map decl_type x).

Definition map_context f c :=
  List.map (map_decl f) c.

Definition map_constant_body f decl :=
  {| cst_type := f decl.(cst_type);
     cst_body := option_map f decl.(cst_body);
     cst_universes := decl.(cst_universes) |}.

Lemma map_cst_type f decl : f (cst_type decl) = cst_type (map_constant_body f decl).

Lemma map_cst_body f decl : option_map f (cst_body decl) = cst_body (map_constant_body f decl).

Definition map_def {A B : Set} (tyf bodyf : A B) (d : def A) :=
  {| dname := d.(dname); dtype := tyf d.(dtype); dbody := bodyf d.(dbody); rarg := d.(rarg) |}.

Lemma map_dtype {A B : Set} (f : A B) (g : A B) (d : def A) :
  f (dtype d) = dtype (map_def f g d).

Lemma map_dbody {A B : Set} (f : A B) (g : A B) (d : def A) :
  g (dbody d) = dbody (map_def f g d).

Definition app_context (Γ Γ' : context) : context := (Γ' ++ Γ)%list.
Notation " Γ ,,, Γ' " := (app_context Γ Γ') (at level 25, Γ' at next level, left associativity) : pcuic.

Lemma app_context_assoc Γ Γ' Γ'' : Γ ,,, (Γ' ,,, Γ'') = Γ ,,, Γ' ,,, Γ''.

Lemma app_context_cons Γ Γ' A : Γ ,,, (Γ' ,, A) = (Γ ,,, Γ') ,, A.

Lemma app_context_length Γ Γ' : #|Γ ,,, Γ'| = #|Γ'| + #|Γ|.

Lemma nth_error_app_context_ge v Γ Γ' : #|Γ'| v nth_error (Γ ,,, Γ') v = nth_error Γ (v - #|Γ'|).

Lemma nth_error_app_context_lt v Γ Γ' : v < #|Γ'| nth_error (Γ ,,, Γ') v = nth_error Γ' v.

Lemma app_context_nil_l Γ : [] ,,, Γ = Γ.

Lemma map_app_context f Γ Γ' : map f (Γ ,,, Γ') = map f Γ ,,, map f Γ'.

Fixpoint decompose_app_rec (t : term) l :=
  match t with
  | tApp f adecompose_app_rec f (a :: l)
  | _(t, l)
  end.

Definition decompose_app t := decompose_app_rec t [].

Lemma decompose_app_rec_mkApps f l l' : decompose_app_rec (mkApps f l) l' =
                                    decompose_app_rec f (l ++ l').


Lemma decompose_app_mkApps f l :
  ~~ isApp f decompose_app (mkApps f l) = (f, l).

Lemma mkApps_nested f l l' : mkApps (mkApps f l) l' = mkApps f (l ++ l').

Fixpoint decompose_prod (t : term) : (list name) × (list term) × term :=
  match t with
  | tProd n A Blet (nAs, B) := decompose_prod B in
                  let (ns, As) := nAs in
                  (n :: ns, A :: As, B)
  | _([], [], t)
  end.

Definition get_ident (n : name) : string :=
  match n with
  | nAnon ⇒ "XX"
  | nNamed ii
  end.

Fixpoint remove_arity (n : nat) (t : term) : term :=
  match n with
  | Ot
  | S nmatch t with
          | tProd _ _ Bremove_arity n B
          | _t
          end
  end.

Definition isConstruct_app t :=
  match fst (decompose_app t) with
  | tConstruct _ _ _true
  | _false
  end.

Fixpoint lookup_mind_decl (id : ident) (decls : global_env)
 := match decls with
    | nilNone
    | InductiveDecl kn d :: tl
      if string_compare kn id is Eq then Some d else lookup_mind_decl id tl
    | _ :: tllookup_mind_decl id tl
    end.

Definition mind_body_to_entry (decl : mutual_inductive_body)
  : mutual_inductive_entry.

Definition arities_context (l : list one_inductive_body) :=
  rev_map (fun indvass (nNamed ind.(ind_name)) ind.(ind_type)) l.

Lemma arities_context_length l : #|arities_context l| = #|l|.

Fixpoint decompose_prod_assum (Γ : context) (t : term) : context × term :=
  match t with
  | tProd n A Bdecompose_prod_assum (Γ ,, vass n A) B
  | tLetIn na b bty b'decompose_prod_assum (Γ ,, vdef na b bty) b'
  | _(Γ, t)
  end.

Fixpoint decompose_prod_n_assum (Γ : context) n (t : term) : option (context × term) :=
  match n with
  | 0 ⇒ Some (Γ, t)
  | S n
    match t with
    | tProd na A Bdecompose_prod_n_assum (Γ ,, vass na A) n B
    | tLetIn na b bty b'decompose_prod_n_assum (Γ ,, vdef na b bty) n b'
    | _None
    end
  end.

Lemma it_mkProd_or_LetIn_app l l' t :
  it_mkProd_or_LetIn (l ++ l') t = it_mkProd_or_LetIn l' (it_mkProd_or_LetIn l t).

Lemma it_mkLambda_or_LetIn_app l l' t :
  it_mkLambda_or_LetIn (l ++ l') t = it_mkLambda_or_LetIn l' (it_mkLambda_or_LetIn l t).

Lemma decompose_prod_n_assum_it_mkProd ctx ctx' ty :
  decompose_prod_n_assum ctx #|ctx'| (it_mkProd_or_LetIn ctx' ty) = Some (ctx' ++ ctx, ty).

Definition is_ind_app_head t :=
  let (f, l) := decompose_app t in
  match f with
  | tInd _ _true
  | _false
  end.

Lemma is_ind_app_head_mkApps ind u l : is_ind_app_head (mkApps (tInd ind u) l).

Lemma decompose_prod_assum_it_mkProd ctx ctx' ty :
  is_ind_app_head ty
  decompose_prod_assum ctx (it_mkProd_or_LetIn ctx' ty) = (ctx' ++ ctx, ty).

Fixpoint reln (l : list term) (p : nat) (Γ0 : list context_decl) {struct Γ0} : list term :=
  match Γ0 with
  | []l
  | {| decl_body := Some _ |} :: hypsreln l (p + 1) hyps
  | {| decl_body := None |} :: hypsreln (tRel p :: l) (p + 1) hyps
  end.

Definition to_extended_list_k Γ k := reln [] k Γ.
Definition to_extended_list Γ := to_extended_list_k Γ 0.

Lemma reln_list_lift_above l p Γ :
  Forall (fun x n, x = tRel n p n n < p + length Γ) l
  Forall (fun x n, x = tRel n p n n < p + length Γ) (reln l p Γ).

Lemma to_extended_list_k_spec Γ k :
  Forall (fun x n, x = tRel n k n n < k + length Γ) (to_extended_list_k Γ k).

Lemma to_extended_list_lift_above Γ :
  Forall (fun x n, x = tRel n n < length Γ) (to_extended_list Γ).

Fixpoint reln_alt p Γ :=
  match Γ with
  | [][]
  | {| decl_body := Some _ |} :: Γreln_alt (p + 1) Γ
  | {| decl_body := None |} :: ΓtRel p :: reln_alt (p + 1) Γ
  end.

Lemma reln_alt_eq l Γ k : reln l k Γ = List.rev (reln_alt k Γ) ++ l.

Lemma to_extended_list_k_cons d Γ k :
  to_extended_list_k (d :: Γ) k =
  match d.(decl_body) with
  | Noneto_extended_list_k Γ (S k) ++ [tRel k]
  | Some bto_extended_list_k Γ (S k)
  end.

Fixpoint context_assumptions (Γ : context) :=
  match Γ with
  | [] ⇒ 0
  | d :: Γ
    match d.(decl_body) with
    | Some _context_assumptions Γ
    | NoneS (context_assumptions Γ)
    end
  end.

Definition map_one_inductive_body npars arities f (n : nat) m :=
  match m with
  | Build_one_inductive_body ind_name ind_type ind_kelim ind_ctors ind_projs
    Build_one_inductive_body ind_name
                             (f 0 ind_type)
                             ind_kelim
                             (map (on_pi2 (f arities)) ind_ctors)
                             (map (on_snd (f (S npars))) ind_projs)
  end.

Definition fold_context f (Γ : context) : context :=
  List.rev (mapi (fun k' declmap_decl (f k') decl) (List.rev Γ)).

Lemma fold_context_alt f Γ :
  fold_context f Γ =
  mapi (fun k' dmap_decl (f (Nat.pred (length Γ) - k')) d) Γ.

Lemma fold_context_length f Γ : length (fold_context f Γ) = length Γ.

Lemma fold_context_snoc0 f Γ d : fold_context f (d :: Γ) = fold_context f Γ ,, map_decl (f (length Γ)) d.

Lemma fold_context_app f Γ Δ :
  fold_context f (Δ ++ Γ) = fold_context (fun kf (length Γ + k)) Δ ++ fold_context f Γ.

Lemma context_assumptions_fold Γ f : context_assumptions (fold_context f Γ) = context_assumptions Γ.

Lemma nth_error_fold_context (f : nat term term):
   (Γ' Γ'' : context) (v : nat),
    v < length Γ' nth,
    nth_error Γ' v = Some nth
    nth_error (fold_context f Γ') v = Some (map_decl (f (length Γ' - S v)) nth).

Lemma nth_error_fold_context_eq:
   (Γ' : context) (v : nat) f,
    nth_error (fold_context f Γ') v =
    option_map (map_decl (f (length Γ' - S v))) (nth_error Γ' v).

Lemma nth_error_ge {Γ Γ' v Γ''} f : length Γ' v
  nth_error (Γ' ++ Γ) v =
  nth_error (fold_context (f 0) Γ' ++ Γ'' ++ Γ) (length Γ'' + v).

Lemma nth_error_lt {Γ Γ' Γ'' v} (f : nat term term) : v < length Γ'
  nth_error (fold_context f Γ' ++ Γ'' ++ Γ) v =
  option_map (map_decl (f (length Γ' - S v))) (nth_error (Γ' ++ Γ) v).

Definition map_mutual_inductive_body f m :=
  match m with
  | Build_mutual_inductive_body finite ind_npars ind_pars ind_bodies ind_universes
    let arities := arities_context ind_bodies in
    let pars := fold_context f ind_pars in
    Build_mutual_inductive_body finite ind_npars pars
      (mapi (map_one_inductive_body (context_assumptions pars) (length arities) f) ind_bodies)
      ind_universes
  end.

Lemma ind_type_map f npars_ass arities n oib :
  ind_type (map_one_inductive_body npars_ass arities f n oib) = f 0 (ind_type oib).

Lemma ind_ctors_map f npars_ass arities n oib :
  ind_ctors (map_one_inductive_body npars_ass arities f n oib) =
  map (on_pi2 (f arities)) (ind_ctors oib).

Lemma ind_pars_map f m :
  ind_params (map_mutual_inductive_body f m) =
  fold_context f (ind_params m).

Lemma ind_projs_map f npars_ass arities n oib :
  ind_projs (map_one_inductive_body npars_ass arities f n oib) =
  map (on_snd (f (S npars_ass))) (ind_projs oib).

Definition test_def {A : Set} (tyf bodyf : A bool) (d : def A) :=
  tyf d.(dtype) && bodyf d.(dbody).

Definition tCaseBrsProp {A} (P : A Type) (l : list (nat × A)) :=
  All (fun xP (snd x)) l.

Definition tFixProp {A : Set} (P P' : A Type) (m : mfixpoint A) :=
  All (fun x : def AP x.(dtype) × P' x.(dbody))%type m.


Lemma map_def_map_def {A B C : Set} (f f' : B C) (g g' : A B) (d : def A) :
  map_def f f' (map_def g g' d) = map_def (fun xf (g x)) (fun xf' (g' x)) d.

Lemma compose_map_def {A B C : Set} (f f' : B C) (g g' : A B) :
  compose (A:=def A) (map_def f f') (map_def g g') = map_def (compose f g) (compose f' g').

Lemma map_def_id {t : Set} x : map_def (@id t) (@id t) x = x.

Lemma map_def_spec {A B : Set} (P P' : A Type) (f f' g g' : A B) (x : def A) :
  P' x.(dbody) P x.(dtype) ( x, P x f x = g x)
  ( x, P' x f' x = g' x)
  map_def f f' x = map_def g g' x.

Lemma case_brs_map_spec {A B : Set} {P : A Type} {l} {f g : A B} :
  tCaseBrsProp P l ( x, P x f x = g x)
  map (on_snd f) l = map (on_snd g) l.

Lemma tfix_map_spec {A B : Set} {P P' : A Type} {l} {f f' g g' : A B} :
  tFixProp P P' l ( x, P x f x = g x)
  ( x, P' x f' x = g' x)
  map (map_def f f') l = map (map_def g g') l.

Lemma map_def_test_spec {A B : Set}
      (P P' : A Type) (p p' : pred A) (f f' g g' : A B) (x : def A) :
  P x.(dtype) P' x.(dbody) ( x, P x p x f x = g x)
  ( x, P' x p' x f' x = g' x)
  test_def p p' x
  map_def f f' x = map_def g g' x.

Lemma case_brs_forallb_map_spec {A B : Set} {P : A Type} {p : A bool}
      {l} {f g : A B} :
  tCaseBrsProp P l
  forallb (test_snd p) l
  ( x, P x p x f x = g x)
  map (on_snd f) l = map (on_snd g) l.

Lemma tfix_forallb_map_spec {A B : Set} {P P' : A Prop} {p p'} {l} {f f' g g' : A B} :
  tFixProp P P' l
  forallb (test_def p p') l
  ( x, P x p x f x = g x)
  ( x, P' x p' x f' x = g' x)
  map (map_def f f') l = map (map_def g g') l.



Lemma mkApps_inj :
   u v l,
    mkApps u l = mkApps v l
    u = v.

Lemma isApp_mkApps :
   u l,
    isApp u
    isApp (mkApps u l).

Lemma decompose_app_rec_notApp :
   t l u l',
    decompose_app_rec t l = (u, l')
    isApp u = false.

Lemma decompose_app_notApp :
   t u l,
    decompose_app t = (u, l)
    isApp u = false.

Fixpoint nApp t :=
  match t with
  | tApp u _S (nApp u)
  | _ ⇒ 0
  end.

Lemma isApp_false_nApp :
   u,
    isApp u = false
    nApp u = 0.

Lemma nApp_mkApps :
   t l,
    nApp (mkApps t l) = nApp t + #|l|.

Lemma decompose_app_eq_mkApps :
   t u l l',
    decompose_app t = (mkApps u l', l)
    l' = [].

Lemma mkApps_nApp_inj :
   u u' l l',
    nApp u = nApp u'
    mkApps u l = mkApps u' l'
    u = u' l = l'.

Lemma mkApps_notApp_inj :
   u u' l l',
    isApp u = false
    isApp u' = false
    mkApps u l = mkApps u' l'
    u = u' l = l'.

Lemma decompose_app_rec_inv {t l' f l} :
  decompose_app_rec t l' = (f, l)
  mkApps t l' = mkApps f l.

Lemma decompose_app_inv {t f l} :
  decompose_app t = (f, l) t = mkApps f l.

Lemma mkApps_Fix_spec mfix idx args t : mkApps (tFix mfix idx) args = t
                                        match decompose_app t with
                                        | (tFix mfix idx, args')args' = args
                                        | _False
                                        end.

Lemma decompose_app_rec_tFix mfix idx args t l :
  decompose_app_rec t l = (tFix mfix idx, args) mkApps t l = mkApps (tFix mfix idx) args.

Lemma decompose_app_tFix mfix idx args t :
  decompose_app t = (tFix mfix idx, args) t = mkApps (tFix mfix idx) args.

Lemma mkApps_size x l : size (mkApps x l) = size x + list_size size l.
Lemma mkApps_eq_head {x l} : mkApps x l = x l = [].

Lemma mkApps_eq_inv {x y l} : x = mkApps y l size y size x.

Lemma mkApps_eq_left x y l : mkApps x l = mkApps y l x = y.

Lemma decompose_app_eq_right t l l' : decompose_app_rec t l = decompose_app_rec t l' l = l'.

Lemma mkApps_eq_right t l l' : mkApps t l = mkApps t l' l = l'.


Lemma atom_decompose_app t l : ~~ isApp t decompose_app_rec t l = pair t l.

Lemma mkApps_eq_inj {t t' l l'} :
  mkApps t l = mkApps t' l'
  ~~ isApp t ~~ isApp t' t = t' l = l'.


Lemma mkApps_eq_decompose {f args t} :
  mkApps f args = t
  ~~ isApp f
  fst (decompose_app t) = f.



Inductive mkApps_spec : term list term term list term term Type :=
| mkApps_intro f l n :
    ~~ isApp f
    mkApps_spec f l (mkApps f (firstn n l)) (skipn n l) (mkApps f l).

Lemma decompose_app_rec_eq f l :
  ~~ isApp f
  decompose_app_rec f l = (f, l).

Lemma firstn_add {A} x y (args : list A) : firstn (x + y) args = firstn x args ++ firstn y (skipn x args).

Lemma decompose_app_rec_inv' f l hd args :
  decompose_app_rec f l = (hd, args)
   n, ~~ isApp hd l = skipn n args f = mkApps hd (firstn n args).

Lemma mkApps_elim_rec t l l' :
  let app' := decompose_app_rec (mkApps t l) l' in
  mkApps_spec app'.1 app'.2 t (l ++ l') (mkApps t (l ++ l')).

Lemma mkApps_elim t l :
  let app' := decompose_app (mkApps t l) in
  mkApps_spec app'.1 app'.2 t l (mkApps t l).

Lemma nisApp_mkApps {t l} : ~~ isApp (mkApps t l) ~~ isApp t l = [].

Lemma mkApps_nisApp {t t' l} : mkApps t l = t' ~~ isApp t' t = t' l = [].

Definition application_atom t :=
  match t with
  | tVar _
  | tSort _
  | tInd _ _
  | tConstruct _ _ _
  | tLambda _ _ _true
  | _false
  end.

Lemma application_atom_mkApps {t l} : application_atom (mkApps t l) application_atom t l = [].


Use a coercion for this common projection of the global context.