Library MetaCoq.SafeChecker.PCUICSafeConversion





Module PSR := PCUICSafeReduce.

Conversion for PCUIC without fuel

Following PCUICSafereduce, we derive a fuel-free implementation of conversion (and cumulativity) checking for PCUIC.

Definition global_uctx (Σ : global_env) : ContextSet.t
  := (global_levels Σ, global_constraints Σ).

Definition global_ext_uctx (Σ : global_env_ext) : ContextSet.t
  := (global_ext_levels Σ, global_ext_constraints Σ).

Definition wf_global_uctx_invariants {cf:checker_flags} Σ
  : wf Σ global_uctx_invariants (global_uctx Σ).

Definition wf_ext_global_uctx_invariants {cf:checker_flags} Σ
  : wf_ext Σ global_uctx_invariants (global_ext_uctx Σ).

Definition global_ext_uctx_consistent {cf:checker_flags} Σ
  : wf_ext Σ consistent (global_ext_uctx Σ).2.
Qed.

Section Conversion.

  Context {cf : checker_flags}.
  Context (flags : RedFlags.t).
  Context (Σ : global_env_ext).
  Context ( : wf Σ ) (Hφ : on_udecl Σ.1 Σ.2 ).
  Context (G : universes_graph) (HG : is_graph_of_uctx G (global_ext_uctx Σ)).



  Inductive state :=
  | Reduction (t : term)
  | Term (t : term)
  | Args
  | Fallback (t : term).

  Inductive stateR : state state Prop :=
  | stateR_Term_Reduction : u v, stateR (Term u) (Reduction v)
  | stateR_Args_Term : u, stateR Args (Term u)
  | stateR_Fallback_Term : u v, stateR (Fallback u) (Term v)
  | stateR_Args_Fallback : u, stateR Args (Fallback u).


  Lemma stateR_Acc :
     s, Acc stateR s.

  Notation wtp Γ t π :=
    (wellformed Σ Γ (zipc t π)) (only parsing).

  Definition wts Γ s t π :=
    match s with
    | Reduction t'
    | Fallback t'
    | Term t'wtp Γ t' π
    | Argswtp Γ t π
    end.


  Record pack (Γ : context) := mkpack {
    st : state ;
    tm : term ;
    stk1 : stack ;
    stk2 : stack ;
    tm' := match st with
           | Reduction t | Fallback t | Term tt
           | Argstm
           end ;
    wth : wellformed Σ Γ (zipc tm' stk2)
  }.


  Definition nlstate (s : state) :=
    match s with
    | Reduction tReduction (nl t)
    | Term tTerm (nl t)
    | ArgsArgs
    | Fallback tFallback (nl t)
    end.

  Definition nl_pack {Γ : context} (p : pack Γ) : pack (nlctx Γ).

  Definition wterm Γ := { t : term | wellformed Σ Γ t }.

  Definition wcored Γ (u v : wterm Γ) :=
    cored' Σ Γ (` u) (` v).

  Lemma wcored_wf :
     Γ, well_founded (wcored Γ).

  Definition eqt u v :=
     eq_term Σ u v .

  Lemma eq_term_valid_pos :
     {u v p},
      validpos u p
      eqt u v
      validpos v p.


  Definition weqt {Γ} (u v : wterm Γ) :=
    eqt (` u) (` v).


  Equations R_aux (Γ : context) :
    ( t : term, pos t × ( w : wterm Γ, pos (` w) × state))
    ( t : term, pos t × ( w : wterm Γ, pos (` w) × state)) Prop :=
    R_aux Γ :=
      t eqt \ cored' Σ Γ by _
      @posR t
      w weqt \ wcored Γ by _
      @posR (` w)
      stateR.



  Lemma R_aux_Acc :
     Γ t p w q s,
      wellformed Σ Γ t
      Acc (R_aux Γ) (t ; (p, (w ; (q, s)))).

  Notation pzt u := (zipc (tm u) (stk1 u)) (only parsing).
  Notation pps1 u := (stack_pos (tm u) (stk1 u)) (only parsing).
  Notation pwt u := (exist _ (wth u)) (only parsing).
  Notation pps2 u := (stack_pos (tm' u) (stk2 u)) (only parsing).

  Notation obpack u :=
    (pzt u ; (pps1 u, (pwt u ; (pps2 u, st u)))) (only parsing).

  Definition R Γ (u v : pack Γ) :=
    R_aux Γ (obpack u) (obpack v).

  Lemma R_Acc :
     Γ u,
      wellformed Σ Γ (zipc (tm u) (stk1 u))
      Acc (R Γ) u.

  Lemma R_cored :
     Γ p1 p2,
      cored Σ Γ (pzt p1) (pzt p2)
      R Γ p1 p2.

  Lemma R_aux_positionR :
     Γ t1 t2 (p1 : pos t1) (p2 : pos t2) s1 s2,
      eq_term Σ t1 t2
      positionR (` p1) (` p2)
      R_aux Γ (t1 ; (p1, s1)) (t2 ; (p2, s2)).

  Lemma R_positionR :
     Γ p1 p2,
      eq_term Σ (pzt p1) (pzt p2)
      positionR (` (pps1 p1)) (` (pps1 p2))
      R Γ p1 p2.

  Lemma R_aux_cored2 :
     Γ t1 t2 (p1 : pos t1) (p2 : pos t2) w1 w2 q1 q2 s1 s2,
      eq_term Σ t1 t2
      ` p1 = ` p2
      cored' Σ Γ (` w1) (` w2)
      R_aux Γ (t1 ; (p1, (w1 ; (q1, s1)))) (t2 ; (p2, (w2 ; (q2, s2)))).

  Lemma R_cored2 :
     Γ p1 p2,
      eq_term Σ (pzt p1) (pzt p2)
      ` (pps1 p1) = ` (pps1 p2)
      cored Σ Γ (` (pwt p1)) (` (pwt p2))
      R Γ p1 p2.

  Lemma R_aux_positionR2 :
     Γ t1 t2 (p1 : pos t1) (p2 : pos t2) w1 w2 q1 q2 s1 s2,
      eq_term Σ t1 t2
      ` p1 = ` p2
      eq_term Σ (` w1) (` w2)
      positionR (` q1) (` q2)
      R_aux Γ (t1 ; (p1, (w1 ; (q1, s1)))) (t2 ; (p2, (w2 ; (q2, s2)))).

  Lemma R_positionR2 :
     Γ p1 p2,
      eq_term Σ (pzt p1) (pzt p2)
      ` (pps1 p1) = ` (pps1 p2)
      eq_term Σ (` (pwt p1)) (` (pwt p2))
      positionR (` (pps2 p1)) (` (pps2 p2))
      R Γ p1 p2.

  Lemma R_aux_stateR :
     Γ t1 t2 (p1 : pos t1) (p2 : pos t2) w1 w2 q1 q2 s1 s2 ,
      eq_term Σ t1 t2
      ` p1 = ` p2
      eq_term Σ (` w1) (` w2)
      ` q1 = ` q2
      stateR s1 s2
      R_aux Γ (t1 ; (p1, (w1 ; (q1, s1)))) (t2 ; (p2, (w2 ; (q2, s2)))).

  Lemma R_stateR :
     Γ p1 p2,
      eq_term Σ (pzt p1) (pzt p2)
      ` (pps1 p1) = ` (pps1 p2)
      eq_term Σ (` (pwt p1)) (` (pwt p2))
      ` (pps2 p1) = ` (pps2 p2)
      stateR (st p1) (st p2)
      R Γ p1 p2.

  Lemma eqb_term_upto_univ_refl :
     (eqb leqb : universe universe bool) t,
      ( u, eqb u u)
      ( u, leqb u u)
      eqb_term_upto_univ eqb leqb t t.

  Definition leqb_term :=
    eqb_term_upto_univ (try_eqb_universe G) (try_leqb_universe G).

  Definition eqb_term :=
    eqb_term_upto_univ (try_eqb_universe G) (try_eqb_universe G).

  Lemma leqb_term_spec t u :
    leqb_term t u
    leq_term (global_ext_constraints Σ) t u.

  Lemma eqb_term_spec t u :
    eqb_term t u
    eq_term (global_ext_constraints Σ) t u.

  Lemma leqb_term_refl :
     t, leqb_term t t.

  Lemma eqb_term_refl :
     t, eqb_term t t.

  Fixpoint eqb_ctx (Γ Δ : context) : bool :=
    match Γ, Δ with
    | [], []true
    | {| decl_name := na1 ; decl_body := None ; decl_type := t1 |} :: Γ,
      {| decl_name := na2 ; decl_body := None ; decl_type := t2 |} :: Δ
      eqb_term t1 t2 && eqb_ctx Γ Δ
    | {| decl_name := na1 ; decl_body := Some b1 ; decl_type := t1 |} :: Γ,
      {| decl_name := na2 ; decl_body := Some b2 ; decl_type := t2 |} :: Δ
      eqb_term b1 b2 && eqb_term t1 t2 && eqb_ctx Γ Δ
    | _, _false
    end.

  Lemma eqb_ctx_spec :
     Γ Δ,
      eqb_ctx Γ Δ
      eq_context_upto (eq_universe (global_ext_constraints Σ)) Γ Δ.

  Definition eqb_term_stack t1 π1 t2 π2 :=
    eqb_ctx (stack_context π1) (stack_context π2) &&
    eqb_term (zipp t1 π1) (zipp t2 π2).

  Lemma eqb_term_stack_spec :
     Γ t1 π1 t2 π2,
      eqb_term_stack t1 π1 t2 π2
      eq_context_upto (eq_universe (global_ext_constraints Σ))
                      (Γ ,,, stack_context π1)
                      (Γ ,,, stack_context π2) ×
      eq_term (global_ext_constraints Σ) (zipp t1 π1) (zipp t2 π2).

  Definition leqb_term_stack t1 π1 t2 π2 :=
    eqb_ctx (stack_context π1) (stack_context π2) &&
    leqb_term (zipp t1 π1) (zipp t2 π2).

  Definition eqb_termp leq u v :=
    match leq with
    | Conveqb_term u v
    | Cumulleqb_term u v
    end.

  Lemma eqb_termp_spec :
     leq u v Γ,
      eqb_termp leq u v
      conv leq Σ Γ u v.

  Lemma leqb_term_stack_spec :
     Γ t1 π1 t2 π2,
      leqb_term_stack t1 π1 t2 π2
      eq_context_upto (eq_universe (global_ext_constraints Σ))
                      (Γ ,,, stack_context π1)
                      (Γ ,,, stack_context π2) ×
      leq_term (global_ext_constraints Σ) (zipp t1 π1) (zipp t2 π2).

  Lemma zwts :
     {Γ s t π},
      wts Γ s t π
      wellformed Σ Γ (zipc match s with Reduction u | Fallback u | Term uu | Argst end π).

  Notation conv_stack_ctx Γ π1 π2 :=
    ( conv_context Σ (Γ ,,, stack_context π1) (Γ ,,, stack_context π2) ).

  Notation conv_term leq Γ t π t' π' :=
    (conv leq Σ (Γ ,,, stack_context π) (zipp t π) (zipp t' π'))
      (only parsing).

  Notation alt_conv_term Γ t π π' :=
    ( Σ ;;; Γ ,,, stack_context π |- zipp t π == zipp t π' )
      (only parsing).


  Definition Ret (s : state) Γ t π π' :=
     (leq : match s with Argsunit | _conv_pb end),
      conv_stack_ctx Γ π π'
      (match s with Fallback t' | Term t'isred (t, π) | _True end)
      (match s with Fallback t' | Term t'isred (t', π') | _True end)
      { b : bool | match s
                return (leq : match s with Argsunit | _conv_pb end), Prop
                with
                | Reduction t'fun leq
                  if b then conv_term leq Γ t π t' π' else True
                | Fallback t'
                | Term t'fun leq
                  if b then conv_term leq Γ t π t' π' else True
                | Argsfun _
                  if b then alt_conv_term Γ t π π' else True
                end leq
      }.

  Definition Aux s Γ t π1 π2 h2 :=
      s' t' π1' π2'
       (h1' : wtp Γ t' π1')
       (h2' : wts Γ s' t' π2'),
       conv_stack_ctx Γ π1 π2
       R Γ
         (mkpack Γ s' t' π1' π2' (zwts h2'))
         (mkpack Γ s t π1 π2 (zwts h2))
       Ret s' Γ t' π1' π2'.

  Notation no := (exist false I) (only parsing).
  Notation yes := (exist true _) (only parsing).

  Notation repack e := (let '(exist b h) := e in exist b _) (only parsing).

  Notation isconv_red_raw leq t1 π1 t2 π2 aux :=
    (aux (Reduction t2) t1 π1 π2 _ _ _ _ leq _ I I) (only parsing).
  Notation isconv_prog_raw leq t1 π1 t2 π2 aux :=
    (aux (Term t2) t1 π1 π2 _ _ _ _ leq _ _ _) (only parsing).
  Notation isconv_args_raw t π1 π2 aux :=
    (aux Args t π1 π2 _ _ _ _ tt _ I I) (only parsing).
  Notation isconv_fallback_raw leq t1 π1 t2 π2 aux :=
    (aux (Fallback t2) t1 π1 π2 _ _ _ _ leq _ _ _) (only parsing).

  Notation isconv_red leq t1 π1 t2 π2 aux :=
    (repack (isconv_red_raw leq t1 π1 t2 π2 aux)) (only parsing).
  Notation isconv_prog leq t1 π1 t2 π2 aux :=
    (repack (isconv_prog_raw leq t1 π1 t2 π2 aux)) (only parsing).
  Notation isconv_args t π1 π2 aux :=
    (repack (isconv_args_raw t π1 π2 aux)) (only parsing).
  Notation isconv_fallback leq t1 π1 t2 π2 aux :=
    (repack (isconv_fallback_raw leq t1 π1 t2 π2 aux)) (only parsing).


  Equations(noeqns) _isconv_red (Γ : context) (leq : conv_pb)
            (t1 : term) (π1 : stack) (h1 : wtp Γ t1 π1)
            (t2 : term) (π2 : stack) (h2 : wtp Γ t2 π2)
            (hx : conv_stack_ctx Γ π1 π2)
            (aux : Aux (Reduction t2) Γ t1 π1 π2 h2)
    : { b : bool | if b then conv_term leq Γ t1 π1 t2 π2 else True } :=

    _isconv_red Γ leq t1 π1 h1 t2 π2 h2 hx aux
    with inspect (decompose_stack π1) := {
    | @exist (args1, ρ1) e1 with inspect (decompose_stack π2) := {
      | @exist (args2, ρ2) e2
        with inspect (reduce_stack nodelta_flags Σ (Γ ,,, stack_context π1) t1 (appstack args1 ε) _) := {
        | @exist (t1',π1') eq1
          with inspect (reduce_stack nodelta_flags Σ (Γ ,,, stack_context π2) t2 (appstack args2 ε) _) := {
          | @exist (t2',π2') eq2isconv_prog leq t1' (π1' +++ ρ1) t2' (π2' +++ ρ2) aux
          }
        }
      }
    }.

  Equations unfold_one_fix (Γ : context) (mfix : mfixpoint term)
            (idx : nat) (π : stack) (h : wtp Γ (tFix mfix idx) π)
    : option (term × stack) :=

    unfold_one_fix Γ mfix idx π h with inspect (unfold_fix mfix idx) := {
    | @exist (Some (arg, fn)) eq1 with inspect (decompose_stack_at π arg) := {
      | @exist (Some (l, c, θ)) eq2 with inspect (reduce_stack RedFlags.default Σ (Γ ,,, stack_context θ) c ε _) := {
        | @exist (cred, ρ) eq3 with construct_viewc cred := {
          | view_construct ind n ui := Some (fn, appstack l (App (zipc (tConstruct ind n ui) ρ) θ)) ;
          | view_other t h := None
          }
        } ;
      | _ := None
      } ;
    | _ := None
    }.


  Lemma unfold_one_fix_red_zipp :
     Γ mfix idx π h fn ξ,
      Some (fn, ξ) = unfold_one_fix Γ mfix idx π h
       red (fst Σ) (Γ ,,, stack_context π) (zipp (tFix mfix idx) π) (zipp fn ξ) .

  Lemma unfold_one_fix_red_zippx :
     Γ mfix idx π h fn ξ,
      Some (fn, ξ) = unfold_one_fix Γ mfix idx π h
       red (fst Σ) Γ (zippx (tFix mfix idx) π) (zippx fn ξ) .

  Lemma unfold_one_fix_red :
     Γ mfix idx π h fn ξ,
      Some (fn, ξ) = unfold_one_fix Γ mfix idx π h
       red (fst Σ) Γ (zipc (tFix mfix idx) π) (zipc fn ξ) .

  Lemma unfold_one_fix_cored :
     Γ mfix idx π h fn ξ,
      Some (fn, ξ) = unfold_one_fix Γ mfix idx π h
      cored (fst Σ) Γ (zipc fn ξ) (zipc (tFix mfix idx) π).

  Lemma unfold_one_fix_decompose :
     Γ mfix idx π h fn ξ,
      Some (fn, ξ) = unfold_one_fix Γ mfix idx π h
      snd (decompose_stack π) = snd (decompose_stack ξ).

  Equations prog_discr (t1 t2 : term) : Prop :=
    prog_discr (tApp _ _) (tApp _ _) := False ;
    prog_discr (tConst _ _) (tConst _ _) := False ;
    prog_discr (tLambda _ _ _) (tLambda _ _ _) := False ;
    prog_discr (tProd _ _ _) (tProd _ _ _) := False ;
    prog_discr (tCase _ _ _ _) (tCase _ _ _ _) := False ;
    prog_discr (tProj _ _) (tProj _ _) := False ;
    prog_discr (tFix _ _) (tFix _ _) := False ;
    prog_discr (tCoFix _ _) (tCoFix _ _) := False ;
    prog_discr _ _ := True.

  Inductive prog_view : term term Set :=
  | prog_view_App u1 v1 u2 v2 :
      prog_view (tApp u1 v1) (tApp u2 v2)

  | prog_view_Const c1 u1 c2 u2 :
      prog_view (tConst c1 u1) (tConst c2 u2)

  | prog_view_Lambda na1 A1 b1 na2 A2 b2 :
      prog_view (tLambda na1 A1 b1) (tLambda na2 A2 b2)

  | prog_view_Prod na1 A1 B1 na2 A2 B2 :
      prog_view (tProd na1 A1 B1) (tProd na2 A2 B2)

  | prog_view_Case ind par p c brs ind' par' p' c' brs' :
      prog_view (tCase (ind, par) p c brs) (tCase (ind', par') p' c' brs')

  | prog_view_Proj p c p' c' :
      prog_view (tProj p c) (tProj p' c')

  | prog_view_Fix mfix idx mfix' idx' :
      prog_view (tFix mfix idx) (tFix mfix' idx')

  | prog_view_CoFix mfix idx mfix' idx' :
      prog_view (tCoFix mfix idx) (tCoFix mfix' idx')

  | prog_view_other :
       u v, prog_discr u v prog_view u v.

  Equations prog_viewc u v : prog_view u v :=
    prog_viewc (tApp u1 v1) (tApp u2 v2) :=
      prog_view_App u1 v1 u2 v2 ;

    prog_viewc (tConst c1 u1) (tConst c2 u2) :=
      prog_view_Const c1 u1 c2 u2 ;

    prog_viewc (tLambda na1 A1 b1) (tLambda na2 A2 b2) :=
      prog_view_Lambda na1 A1 b1 na2 A2 b2 ;

    prog_viewc (tProd na1 A1 B1) (tProd na2 A2 B2) :=
      prog_view_Prod na1 A1 B1 na2 A2 B2 ;

    prog_viewc (tCase (ind, par) p c brs) (tCase (ind', par') p' c' brs') :=
      prog_view_Case ind par p c brs ind' par' p' c' brs' ;

    prog_viewc (tProj p c) (tProj p' c') :=
      prog_view_Proj p c p' c' ;

    prog_viewc (tFix mfix idx) (tFix mfix' idx') :=
      prog_view_Fix mfix idx mfix' idx' ;

    prog_viewc (tCoFix mfix idx) (tCoFix mfix' idx') :=
      prog_view_CoFix mfix idx mfix' idx' ;

    prog_viewc u v := prog_view_other u v I.

  Lemma elimT (P : Type) (b : bool) : reflectT P b b P.

  Lemma introT (P : Type) (b : bool) : reflectT P b P b.

  Lemma wellformed_wf_local Γ t : wellformed Σ Γ t wf_local Σ Γ .

  Equations(noeqns) unfold_constants (Γ : context) (leq : conv_pb)
            (c : kername) (u : universe_instance) (π1 : stack)
            (h1 : wtp Γ (tConst c u) π1)
            (c' : kername) (u' : universe_instance) (π2 : stack)
            (h2 : wtp Γ (tConst c' u') π2)
            (hx : conv_stack_ctx Γ π1 π2)
            (aux : Aux (Term (tConst c' u')) Γ (tConst c u) π1 π2 h2)
    : { b : bool | if b then conv_term leq Γ (tConst c u) π1 (tConst c' u') π2 else True } :=

    unfold_constants Γ leq c u π1 h1 c' u' π2 h2 hx aux
    with inspect (lookup_env Σ c') := {
    | @exist (Some (ConstantDecl n {| cst_body := Some b |})) eq1 :=
      isconv_red leq (tConst c u) π1 (subst_instance_constr u' b) π2 aux ;
    
    | _ with inspect (lookup_env Σ c) := {
      | @exist (Some (ConstantDecl n {| cst_body := Some b |})) eq1 :=
        isconv_red leq (subst_instance_constr u b) π1
                        (tConst c' u') π2 aux ;
      
      | _ := no
      }
    }.

  Definition eqb_universe_instance u v :=
    forallb2 (try_eqb_universe G) (map Universe.make u) (map Universe.make v).

  Lemma eqb_universe_instance_spec :
     u v,
      eqb_universe_instance u v
      R_universe_instance (eq_universe (global_ext_constraints Σ)) u v.

  Lemma wellformed_eq_term :
     Γ u v,
      wellformed Σ Γ u
      eq_term Σ u v
      wellformed Σ Γ v.

  Equations(noeqns) _isconv_prog (Γ : context) (leq : conv_pb)
            (t1 : term) (π1 : stack) (h1 : wtp Γ t1 π1)
            (t2 : term) (π2 : stack) (h2 : wtp Γ t2 π2)
            (hx : conv_stack_ctx Γ π1 π2)
            (ir1 : isred (t1, π1)) (ir2 : isred (t2, π2))
            (aux : Aux (Term t2) Γ t1 π1 π2 h2)
    : { b : bool | if b then conv_term leq Γ t1 π1 t2 π2 else True } :=

    _isconv_prog Γ leq t1 π1 h1 t2 π2 h2 hx ir1 ir2 aux with prog_viewc t1 t2 := {
    | prog_view_App _ _ _ _ :=
      False_rect _ _ ;

    | prog_view_Const c u c' u' with eq_dec c c' := {
      | left eq1 with inspect (eqb_universe_instance u u') := {
        | @exist true eq2 with isconv_args_raw (tConst c u) π1 π2 aux := {
          | @exist true h := yes ;
          
          | @exist false _ with inspect (lookup_env Σ c) := {
            | @exist (Some (ConstantDecl n {| cst_body := Some body |})) eq3 :=
              isconv_red leq (subst_instance_constr u body) π1
                             (subst_instance_constr u body) π2 aux ;
            
            | @exist _ _ := no
            }
          } ;
        
        | @exist false _ := unfold_constants Γ leq c u π1 h1 c' u' π2 h2 hx aux
        } ;
      
      | right _ := unfold_constants Γ leq c u π1 h1 c' u' π2 h2 hx aux
      } ;

    | prog_view_Lambda na A1 t1 na' A2 t2
      with isconv_red_raw Conv A1 (Lambda_ty na t1 π1)
                                 A2 (Lambda_ty na' t2 π2) aux := {
      | @exist true h :=
        isconv_red leq
                   t1 (Lambda_tm na A1 π1)
                   t2 (Lambda_tm na' A2 π2) aux ;
      | @exist false _ := no
      } ;

    | prog_view_Prod na A1 B1 na' A2 B2
      with isconv_red_raw Conv A1 (Prod_l na B1 π1) A2 (Prod_l na' B2 π2) aux := {
      | @exist true h :=
        isconv_red leq
                   B1 (Prod_r na A1 π1)
                   B2 (Prod_r na' A2 π2) aux ;
      | @exist false _ := no
      } ;

    | prog_view_Case ind par p c brs ind' par' p' c' brs'
      with inspect (eqb_term (tCase (ind, par) p c brs) (tCase (ind', par') p' c' brs')) := {
      | @exist true eq1 := isconv_args (tCase (ind, par) p c brs) π1 π2 aux ;
      | @exist false _ with inspect (reduce_term RedFlags.default Σ (Γ ,,, stack_context π1) c _) := {
        | @exist cred eq1 with inspect (reduce_term RedFlags.default Σ (Γ ,,, stack_context π2) c' _) := {
           | @exist cred' eq2 with inspect (eqb_term cred c && eqb_term cred' c') := {
              | @exist true eq3 := no ;
              | @exist false eq3 :=
                isconv_red leq (tCase (ind, par) p cred brs) π1
                               (tCase (ind', par') p' cred' brs') π2 aux
              }
           }
        }
      } ;

    | prog_view_Proj p c p' c' with inspect (eqb_term (tProj p c) (tProj p' c')) := {
      | @exist true eq1 := isconv_args (tProj p c) π1 π2 aux ;
      | @exist false _ := no
      } ;

    | prog_view_Fix mfix idx mfix' idx'
      with inspect (eqb_term (tFix mfix idx) (tFix mfix' idx')) := {
      | @exist true eq1 := isconv_args (tFix mfix idx) π1 π2 aux ;
      | @exist false _ with inspect (unfold_one_fix Γ mfix idx π1 _) := {
        | @exist (Some (fn, θ)) eq1
          with inspect (decompose_stack θ) := {
          | @exist (l', θ') eq2
            with inspect (reduce_stack nodelta_flags Σ (Γ ,,, stack_context θ') fn (appstack l' ε) _) := {
            | @exist (fn', ρ) eq3 :=
              isconv_prog leq fn'+++ θ') (tFix mfix' idx') π2 aux
            }
          } ;
        | _ with inspect (unfold_one_fix Γ mfix' idx' π2 _) := {
          | @exist (Some (fn, θ)) eq1
            with inspect (decompose_stack θ) := {
            | @exist (l', θ') eq2
              with inspect (reduce_stack nodelta_flags Σ (Γ ,,, stack_context θ') fn (appstack l' ε) _) := {
              | @exist (fn', ρ) eq3 :=
                isconv_prog leq (tFix mfix idx) π1 fn'+++ θ') aux
              }
            } ;
          | _ := no
          }
        }
      } ;

    | prog_view_CoFix mfix idx mfix' idx'
      with inspect (eqb_term (tCoFix mfix idx) (tCoFix mfix' idx')) := {
      | @exist true eq1 := isconv_args (tCoFix mfix idx) π1 π2 aux ;
      | @exist false _ := no
      } ;

    | prog_view_other t1 t2 h :=
      isconv_fallback leq t1 π1 t2 π2 aux
    }.










  Definition Aux' Γ t args l1 π1 π2 h2 :=
      u1 u2 ca1 a1 ρ2
       (h1' : wtp Γ u1 (coApp (mkApps t ca1) (appstack a1 π1)))
       (h2' : wtp Γ u2 ρ2),
       let x := mkpack Γ (Reduction u2) u1 (coApp (mkApps t ca1) (appstack a1 π1)) ρ2 h2' in
       let y := mkpack Γ Args (mkApps t args) (appstack l1 π1) π2 h2 in
       (S #|ca1| + #|a1| = #|args| + #|l1|)%nat
       pzt x = pzt y
       positionR (` (pps1 x)) (` (pps1 y))
       Ret (Reduction u2) Γ u1 (coApp (mkApps t ca1) (appstack a1 π1)) ρ2.

  Equations(noeqns) _isconv_args' (Γ : context) (t : term) (args : list term)
            (l1 : list term) (π1 : stack)
            (h1 : wtp Γ (mkApps t args) (appstack l1 π1))
            (hπ1 : isStackApp π1 = false)
            (l2 : list term) (π2 : stack)
            (h2 : wtp Γ (mkApps t args) (appstack l2 π2))
            (hπ2 : isStackApp π2 = false)
            (hx : conv_stack_ctx Γ π1 π2)
            (aux : Aux' Γ t args l1 π1 (appstack l2 π2) h2)
    : { b : bool | if b then alt_conv_term Γ (mkApps t args) (appstack l1 π1) (appstack l2 π2) else True } by struct l1 :=
    _isconv_args' Γ t args (u1 :: l1) π1 h1 hπ1 (u2 :: l2) π2 h2 hπ2 hx aux
    with aux u1 u2 args l1 (coApp (mkApps t args) (appstack l2 π2)) _ _ _ _ Conv _ I I := {
    | @exist true H1 with _isconv_args' Γ t (args ++ [u1]) l1 π1 _ _ l2 π2 _ _ _ _ := {
      | @exist true H2 := yes ;
      | @exist false _ := no
      } ;
    | @exist false _ := no
    } ;

    _isconv_args' Γ t args [] π1 h1 hπ1 [] π2 h2 hπ2 hx aux := yes ;

    _isconv_args' Γ t args l1 π1 h1 hπ1 l2 π2 h2 hπ2 hx aux := no.

  Equations(noeqns) _isconv_args (Γ : context) (t : term)
           (π1 : stack) (h1 : wtp Γ t π1)
           (π2 : stack) (h2 : wtp Γ t π2)
           (hx : conv_stack_ctx Γ π1 π2)
           (aux : Aux Args Γ t π1 π2 h2)
    : { b : bool | if b then alt_conv_term Γ t π1 π2 else True } :=
    _isconv_args Γ t π1 h1 π2 h2 hx aux with inspect (decompose_stack π1) := {
    | @exist (l1, θ1) eq1 with inspect (decompose_stack π2) := {
      | @exist (l2, θ2) eq2 with _isconv_args' Γ t [] l1 θ1 _ _ l2 θ2 _ _ _ _ := {
        | @exist true h := yes ;
        | @exist false _ := no
        }
      }
    }.

  Equations unfold_one_case (Γ : context) (ind : inductive) (par : nat)
            (p c : term) (brs : list (nat × term))
            (h : wellformed Σ Γ (tCase (ind, par) p c brs)) : option term :=
    unfold_one_case Γ ind par p c brs h
    with inspect (reduce_stack RedFlags.default Σ Γ c ε _) := {
    | @exist (cred, ρ) eq with cc_viewc cred := {
      | ccview_construct ind' n ui with inspect (decompose_stack ρ) := {
        | @exist (args, ξ) eq' := Some (iota_red par n args brs)
        } ;
      | ccview_cofix mfix idx with inspect (unfold_cofix mfix idx) := {
        | @exist (Some (narg, fn)) eq2 with inspect (decompose_stack ρ) := {
          | @exist (args, ξ) eq' := Some (tCase (ind, par) p (mkApps fn args) brs)
          } ;
        | @exist None eq2 := None
        } ;
      | ccview_other t _ := None
      }
    }.

  Lemma unfold_one_case_cored :
     Γ ind par p c brs h t,
      Some t = unfold_one_case Γ ind par p c brs h
      cored Σ Γ t (tCase (ind, par) p c brs).

  Equations unfold_one_proj (Γ : context) (p : projection) (c : term)
            (h : wellformed Σ Γ (tProj p c)) : option term :=

    unfold_one_proj Γ p c h with p := {
    | (i, pars, narg) with inspect (reduce_stack RedFlags.default Σ Γ c ε _) := {
      | @exist (cred, ρ) eq with cc_viewc cred := {
        | ccview_construct ind' n ui with inspect (decompose_stack ρ) := {
          | @exist (args, ξ) eq' with inspect (nth_error args (pars + narg)) := {
            | @exist (Some arg) eq2 := Some arg ;
            | @exist None _ := None
            }
          } ;
        | ccview_cofix mfix idx with inspect (decompose_stack ρ) := {
          | @exist (args, ξ) eq' with inspect (unfold_cofix mfix idx) := {
            | @exist (Some (narg, fn)) eq2 :=
              Some (tProj (i, pars, narg) (mkApps fn args)) ;
            | @exist None eq2 := None
            }
          } ;
        | ccview_other t _ := None
        }
      }
    }.

  Lemma unfold_one_proj_cored :
     Γ p c h t,
      Some t = unfold_one_proj Γ p c h
      cored Σ Γ t (tProj p c).

  Equations reducible_head (Γ : context) (t : term) (π : stack)
            (h : wtp Γ t π)
    : option (term × stack) :=

    reducible_head Γ (tFix mfix idx) π h := unfold_one_fix Γ mfix idx π h ;

    reducible_head Γ (tCase (ind, par) p c brs) π h
    with inspect (unfold_one_case (Γ ,,, stack_context π) ind par p c brs _) := {
    | @exist (Some t) eq :=Some (t, π) ;
    | @exist None _ := None
    } ;

    reducible_head Γ (tProj p c) π h
    with inspect (unfold_one_proj (Γ ,,, stack_context π) p c _) := {
    | @exist (Some t) eq := Some (t, π) ;
    | @exist None _ := None
    } ;

    reducible_head Γ (tConst c u) π h
    with inspect (lookup_env Σ c) := {
    | @exist (Some (ConstantDecl _ {| cst_body := Some body |})) eq :=
      Some (subst_instance_constr u body, π) ;
    | @exist _ _ := None
    } ;

    reducible_head Γ _ π h := None.

  Lemma reducible_head_red_zipp :
     Γ t π h fn ξ,
      Some (fn, ξ) = reducible_head Γ t π h
       red (fst Σ) (Γ ,,, stack_context π) (zipp t π) (zipp fn ξ) .

  Lemma reducible_head_red_zippx :
     Γ t π h fn ξ,
      Some (fn, ξ) = reducible_head Γ t π h
       red (fst Σ) Γ (zippx t π) (zippx fn ξ) .

  Lemma reducible_head_cored :
     Γ t π h fn ξ,
      Some (fn, ξ) = reducible_head Γ t π h
      cored Σ Γ (zipc fn ξ) (zipc t π).

  Lemma reducible_head_decompose :
     Γ t π h fn ξ,
      Some (fn, ξ) = reducible_head Γ t π h
      snd (decompose_stack π) = snd (decompose_stack ξ).

  Equations(noeqns) _isconv_fallback (Γ : context) (leq : conv_pb)
            (t1 : term) (π1 : stack) (h1 : wtp Γ t1 π1)
            (t2 : term) (π2 : stack) (h2 : wtp Γ t2 π2)
            (ir1 : isred (t1, π1)) (ir2 : isred (t2, π2))
            (hx : conv_stack_ctx Γ π1 π2)
            (aux : Aux (Fallback t2) Γ t1 π1 π2 h2)
    : { b : bool | if b then conv_term leq Γ t1 π1 t2 π2 else True } :=
    _isconv_fallback Γ leq t1 π1 h1 t2 π2 h2 ir1 ir2 hx aux
    with inspect (reducible_head Γ t1 π1 h1) := {
    | @exist (Some (rt1, ρ1)) eq1 with inspect (decompose_stack ρ1) := {
      | @exist (l1, θ1) eq2
        with inspect (reduce_stack nodelta_flags Σ (Γ ,,, stack_context ρ1) rt1 (appstack l1 ε) _) := {
        | @exist (rt1', θ1') eq3 :=
          isconv_prog leq rt1' (θ1' +++ θ1) t2 π2 aux
        }
      } ;
    | @exist None _ with inspect (reducible_head Γ t2 π2 h2) := {
      | @exist (Some (rt2, ρ2)) eq1 with inspect (decompose_stack ρ2) := {
        | @exist (l2, θ2) eq2
          with inspect (reduce_stack nodelta_flags Σ (Γ ,,, stack_context ρ2) rt2 (appstack l2 ε) _) := {
          | @exist (rt2', θ2') eq3 :=
            isconv_prog leq t1 π1 rt2' (θ2' +++ θ2) aux
          }
        } ;
      | @exist None _ with inspect leq := {
        | @exist Conv eq1 with inspect (eqb_term t1 t2) := {
          | @exist true eq2 := isconv_args t1 π1 π2 aux ;
          | @exist false _ := no
          } ;
        | @exist Cumul eq1 with inspect (eqb_term t1 t2) := {
          | @exist true eq2 := isconv_args t1 π1 π2 aux ;
          | @exist false _ := exist (leqb_term (zipp t1 π1) (zipp t2 π2)) _
          }
        }
      }
    }.

  Equations _isconv (s : state) (Γ : context)
            (t : term) (π1 : stack) (h1 : wtp Γ t π1)
            (π2 : stack) (h2 : wts Γ s t π2)
            (aux : Aux s Γ t π1 π2 h2)
  : Ret s Γ t π1 π2 :=
    _isconv (Reduction t2) Γ t1 π1 h1 π2 h2 aux :=
      λ { | leq | hx | _ | _ := _isconv_red Γ leq t1 π1 h1 t2 π2 h2 hx aux } ;

    _isconv (Term t2) Γ t1 π1 h1 π2 h2 aux :=
      λ { | leq | hx | r1 | r2 := _isconv_prog Γ leq t1 π1 h1 t2 π2 h2 hx r1 r2 aux } ;

    _isconv Args Γ t π1 h1 π2 h2 aux :=
      λ { | _ | hx | _ | _ := _isconv_args Γ t π1 h1 π2 h2 hx aux } ;

    _isconv (Fallback t2) Γ t1 π1 h1 π2 h2 aux :=
      λ { | leq | hx | r1 | r2 := _isconv_fallback Γ leq t1 π1 h1 t2 π2 h2 r1 r2 hx aux }.


  Equations(noeqns) isconv_full (s : state) (Γ : context)
            (t : term) (π1 : stack) (h1 : wtp Γ t π1)
            (π2 : stack) (h2 : wts Γ s t π2)
    : Ret s Γ t π1 π2 :=
    isconv_full s Γ t π1 h1 π2 h2 hx :=
      Fix_F (R := R Γ)
            (fun ppwtp Γ (tm pp) (stk1 pp) wts Γ (st pp) (tm pp) (stk2 pp)
                     Ret (st pp) Γ (tm pp) (stk1 pp) (stk2 pp)
                    )
            (fun pp f_)
            (x := mkpack Γ s t π1 π2 _)
            _ _ _ _.

  Definition isconv Γ leq t1 π1 h1 t2 π2 h2 hx :=
    let '(exist b _) :=
      isconv_full (Reduction t2) Γ t1 π1 h1 π2 h2 leq hx I I
    in b.

  Theorem isconv_sound :
     Γ leq t1 π1 h1 t2 π2 h2 hx,
      isconv Γ leq t1 π1 h1 t2 π2 h2 hx
      conv leq Σ (Γ ,,, stack_context π1) (zipp t1 π1) (zipp t2 π2).

  Definition isconv_term Γ leq t1 (h1 : wellformed Σ Γ t1) t2 (h2 : wellformed Σ Γ t2) :=
    isconv Γ leq t1 ε h1 t2 ε h2 (sq (conv_ctx_refl _ Γ)).

  Theorem isconv_term_sound :
     Γ leq t1 h1 t2 h2,
      isconv_term Γ leq t1 h1 t2 h2
      conv leq Σ Γ t1 t2.

End Conversion.