Equivalence of String Rewriting and Satisfiability of words - Attempt of Forward direction


From Coq Require Import Lia List Init.Nat.
From PCF2.Autosubst Require Import pcf_2 unscoped.
From PCF2 Require Import pcf_2_system pcf_2_contexts pcf_2_utils CE CE_facts SATIS SATIS_facts preliminaries.
From PCF2.external Require Import SR.
From PCF2.external.Synthetic Require Import Definitions.
Set Default Goal Selector "!".

Definition ren_shift (d1 d2 e f: str) :=
  fun n =>
  if ltb n (2 * length d1) then 2 * length e + 2 + n else
    if ltb n (2 * (length d1 + length e)) then n - 2 * length d1 else
      if ltb n (2 * (length d1 + length e + length d2)) then
        2 * (length e) + 2 + (n - 2 * diff (length e) (length f)) else
          if ltb n (2 * (length d1 + length e + length d2) + 2) then
            n - 2 * (length d1 + length d2) else
            n + 2 * length f + 2.

Lemma ren_shift_well_typed (d1 d2 e f: str) R W0:
  ren_well_typed (base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2) ++ make_rule_types R ++ T W0 :: nil) (base_context (2 * length (d1 ++ e ++ d2) + 2) ++ make_rule_types R ++ T W0 :: nil) (ren_shift d1 d2 e f).
Proof.
  intros n A H. assert (eq1: n < 2 * length (d1 ++ e ++ d2) + 2 + length R + 1).
  { assert (eq1: 2 * length (d1 ++ e ++ d2) + 2 + length R + 1 = length (base_context (2 * length (d1 ++ e ++ d2) + 2) ++ make_rule_types R ++ T W0 :: nil)).
    - repeat rewrite app_length. rewrite length_base_context. rewrite make_rule_types_length. cbn. lia.
    - rewrite eq1. apply nth_error_Some. intros H1. now rewrite H1 in H. }
  unfold ren_shift. destruct (ltb n (2 * length d1)) eqn: eq2.
  { rewrite ltb_lt_true in eq2. rewrite nth_error_app2. 2: rewrite length_base_context; lia.
    rewrite nth_error_app1 in H. 2: rewrite length_base_context; simpl_list; lia.
    rewrite length_base_context. erewrite (base_context_some_base _ _ A). 2: eauto. rewrite nth_error_app1. 2: rewrite length_base_context; simpl_list; lia. apply base_context_le_base. simpl_list. lia. }
    rewrite ltb_lt_false in eq2. destruct (ltb n (2 * (length d1 + length e))) eqn: eq3.
  { rewrite ltb_lt_true in eq3. rewrite nth_error_app1. 2: rewrite length_base_context; lia.
  rewrite nth_error_app1 in H. 2: rewrite length_base_context; simpl_list; lia.
  erewrite (base_context_some_base _ _ A). 2: eauto. apply base_context_le_base. simpl_list. lia. }
  rewrite ltb_lt_false in eq3. destruct (ltb n (2 * (length d1 + length e + length d2))) eqn: eq4.
  { rewrite ltb_lt_true in eq4. destruct (nat_ord_dec (length f) (length e)) as [l | l].
    - specialize (diff1 _ _ l). intros H1. rewrite nth_error_app2. 2: rewrite H1; rewrite length_base_context; lia.
    rewrite nth_error_app1 in H. 2: rewrite length_base_context; simpl_list; lia.
    rewrite length_base_context. erewrite (base_context_some_base _ _ A). 2: eauto.
    rewrite nth_error_app1. 2: rewrite length_base_context, H1; simpl_list.
    2: { destruct (arith_technical' (2 * (length d1 + length e)) n (2 * length d2)) as [n' [H2 H3]]. 1,2 : lia.
         rewrite H3. lia. } apply base_context_le_base. simpl_list.
    rewrite H1. lia.
    - specialize (diff2 _ _ l). intros H1. rewrite nth_error_app2. 2: rewrite H1; rewrite length_base_context; lia.
    rewrite nth_error_app1 in H. 2: rewrite length_base_context; simpl_list; lia.
    rewrite length_base_context. erewrite (base_context_some_base _ _ A). 2: eauto.
    rewrite nth_error_app1. 2: rewrite H1, length_base_context; simpl_list; lia. apply base_context_le_base. simpl_list.
    rewrite H1. lia. }
    rewrite ltb_lt_false in eq4. destruct (ltb n (2 * (length d1 + length e + length d2) + 2)) eqn: eq5.
    - rewrite ltb_lt_true in eq5. rewrite nth_error_app1. 2: rewrite length_base_context; lia.
      rewrite nth_error_app1 in H. 2: rewrite length_base_context; simpl_list; lia.
    erewrite (base_context_some_base _ _ A). 2: eauto. apply base_context_le_base. simpl_list. lia.
    - rewrite ltb_lt_false in eq5. repeat rewrite app_length in eq1. rewrite nth_error_app2. 2: rewrite length_base_context; lia.
      rewrite nth_error_app2. 2: repeat rewrite length_base_context; simpl_list; lia.
      rewrite nth_error_app2 in H. 2: rewrite length_base_context; simpl_list; lia.
      rewrite length_base_context in H. repeat rewrite length_base_context.
      assert (eq6: n + 2 * length f + 2 - (2 * length e + 2) - (2 * length (d1 ++ f ++ d2) + 2) = n - (2 * length (d1 ++ e ++ d2) + 2)) by now simpl_list; lia.
      now rewrite eq6.
Qed.

Lemma ren_shift_well_typed' (d1 d2 e f: str):
  ren_well_typed (base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2)) (base_context (2 * length (d1 ++ e ++ d2) + 2)) (ren_shift d1 d2 e f).
Proof.
  intros n A H. assert (eq1: n < 2 * length (d1 ++ e ++ d2) + 2).
  { rewrite <- length_base_context. apply nth_error_Some. intros H1. now rewrite H1 in H. }
  unfold ren_shift. destruct (ltb n (2 * length d1)) eqn: eq2.
  { rewrite ltb_lt_true in eq2. rewrite nth_error_app2. 2: rewrite length_base_context; lia.
    rewrite length_base_context. erewrite (base_context_some_base _ _ A). 2: eauto. apply base_context_le_base. simpl_list. lia. }
  rewrite ltb_lt_false in eq2. destruct (ltb n (2 * (length d1 + length e))) eqn: eq3.
  { rewrite ltb_lt_true in eq3. rewrite nth_error_app1. 2: rewrite length_base_context; lia.
  erewrite (base_context_some_base _ _ A). 2: eauto. apply base_context_le_base. simpl_list. lia. }
  rewrite ltb_lt_false in eq3. destruct (ltb n (2 * (length d1 + length e + length d2))) eqn: eq4.
  { rewrite ltb_lt_true in eq4. destruct (nat_ord_dec (length f) (length e)) as [l | l].
    - specialize (diff1 _ _ l). intros H1. rewrite nth_error_app2. 2: rewrite H1; rewrite length_base_context; lia.
    rewrite length_base_context. erewrite (base_context_some_base _ _ A). 2: eauto. apply base_context_le_base. simpl_list.
    rewrite H1. lia.
    - specialize (diff2 _ _ l). intros H1. rewrite nth_error_app2. 2: rewrite H1; rewrite length_base_context; lia.
    rewrite length_base_context. erewrite (base_context_some_base _ _ A). 2: eauto. apply base_context_le_base. simpl_list.
    rewrite H1. lia. }
    rewrite ltb_lt_false in eq4. destruct (ltb n (2 * (length d1 + length e + length d2) + 2)) eqn: eq5.
    - rewrite ltb_lt_true in eq5. rewrite nth_error_app1. 2: rewrite length_base_context; lia.
    erewrite (base_context_some_base _ _ A). 2: eauto. apply base_context_le_base. simpl_list. lia.
    - rewrite ltb_lt_false in eq5. repeat rewrite app_length in eq1. lia.
Qed.

Lemma t_subst_map_var_id'' (enc: str -> tm) (R: list (str * str)) (F: str * str -> tm) (W0 d1 f d2: str):
  map (Subst_tm (t_subst enc R F W0 (d1 ++ f ++ d2))) (var_seq (2 * length d1) (2 * length f) ++ var_seq (2 * length (d1 ++ f ++ d2)) 2) = var_seq (2 * length d1) (2 * length f) ++ var_seq (2 * length (d1 ++ f ++ d2)) 2.
Proof.
  apply list_eq.
  1: now rewrite map_length.
  intros n a a' H1 H2.
  rewrite nth_error_map in H1.
  assert (H3: n < 2 * length f + 2).
  { assert (length (var_seq (2 * length d1) (2 * length f) ++ var_seq (2 * length (d1 ++ f ++ d2)) 2) = 2 * length f + 2) by now rewrite app_length, length_var_seq, length_var_seq; lia.
    rewrite <- H. apply nth_error_Some. intros H3. now rewrite H3 in H2. }
  destruct (ltb n (2 * length f)) eqn: e1.
  { rewrite ltb_lt_true in e1. rewrite nth_error_app1 in H2. 2: rewrite length_var_seq; lia.
    rewrite var_seq_nth in H2. 2: easy. injection H2. intros ?; subst. rewrite nth_error_app1 in H1. 2: now rewrite length_var_seq.
    rewrite var_seq_nth in H1. 2: easy. cbn in H1. injection H1. intros ?. subst. unfold t_subst.
    destruct (leb (length d1 + (length d1 + 0) + n)(2 * length (d1 ++ f ++ d2) + 1)) eqn: e2. 1: easy.
    rewrite leb_le_false in e2. repeat rewrite app_length in e2. lia. }
    rewrite ltb_lt_false in e1. rewrite nth_error_app2 in H2. 2: rewrite length_var_seq; lia.
    rewrite nth_error_app2 in H1. 2: rewrite length_var_seq; lia.
    rewrite var_seq_nth in H2. 2: rewrite length_var_seq; lia.
    rewrite var_seq_nth in H1. 2: rewrite length_var_seq; lia.
    injection H2. injection H1. intros ? ?. subst. repeat rewrite length_var_seq.
    unfold t_subst. destruct (leb (length (d1 ++ f ++ d2) + (length (d1 ++ f ++ d2) + 0) + (n - (length f + (length f + 0))))(2 * length (d1 ++ f ++ d2) + 1)) eqn: e2.
    1: easy. rewrite leb_le_false in e2. lia.
Qed.

Lemma t_subst_rule (enc: str -> tm) (R: list (str * str)) (F: str * str -> tm) (W0 W: str) (p: str * str) (n: nat):
  nth_error R n = Some p -> (t_subst enc R F W0 W) (2 * length W + 2 + n) = F p.
Proof.
  intros H. unfold t_subst. destruct (leb (2 * length W + 2 + n) (2 * length W + 1)) eqn: e1.
  { rewrite leb_le_true in e1. lia. }
  rewrite leb_le_false in e1. destruct (leb (2 * length W + 2 + n) (2 * length W + 1 + length R)) eqn: e2.
  - assert (eq: 2 * length W + 2 + n - (2 * length W + 2) = n) by lia. destruct (nth_error R (2 * length W + 2 + n - (2 * length W + 2))) eqn: e3.
    + rewrite eq in e3. rewrite e3 in H.
      injection H. intros?. now subst.
    + rewrite eq in e3. rewrite e3 in H. discriminate.
  - rewrite leb_le_false in e2. enough (n < length R) by lia.
    apply nth_error_Some. intros H'. rewrite H' in H. discriminate.
Qed.

Lemma ren_shift_typed (t: tm) (d1 d2 e f: str) (R: SRS bool) (W0: str):
  (base_context (2 * length (d1 ++ e ++ d2) + 2) ++ make_rule_types R ++ T W0 :: nil) ⊢ t: Base
  -> (base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2) ++ make_rule_types R ++ T W0 :: nil) ⊢ ren_tm (ren_shift d1 d2 e f) t: Base.
Proof.
  intros H. eapply ren_preserves'. 2: exact H. eapply ren_shift_well_typed.
Qed.

Lemma ren_shift_shifted_t_subst_char enc R F (d1 e f d2 W0: str) t:
  Subst_tm (iter (2 * length e + 2) Up_tm_tm (t_subst enc R F W0 (d1 ++ f ++ d2))) (ren_tm (ren_shift d1 d2 e f) t) = ren_tm (ren_shift d1 d2 e f) (Subst_tm (t_subst enc R F W0 (d1 ++ e ++ d2)) t).
Proof.
  asimpl. apply subst_charact.
  intros n. unfold ren_shift, t_subst, core.funcomp.
Admitted.

Lemma ren_var d1 e f d2:
  map (ren_tm (ren_shift d1 d2 e f)) (var_seq 0 (2 * length (d1 ++ e ++ d2) + 2)) = var_seq (2 * length e + 2) (2 * length d1) ++ var_seq 0 (2 * length e) ++ var_seq (2 * (length e + length d1 + length f) + 2) (2 * length d2) ++ var_seq (2 * length e) 2.
Proof.
Admitted.

Lemma derived_satis_ineq (d1 e f d2: str) enc R W0 F t v:
  word_encoding enc v -> (base_context (2 * length (d1 ++ e ++ d2) + 2)) ⊢ apply_args (enc (d1 ++ e ++ d2)) (var_seq 0 (2 * length (d1 ++ e ++ d2) + 2)) ≤ Subst_tm (t_subst enc R F W0 (d1 ++ e ++ d2)) t : Base
  -> (base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2)) ⊢ apply_args (enc (d1 ++ e ++ d2)) (var_seq (2 * length e + 2) (2 * length d1) ++ var_seq 0 (2 * length e) ++ var_seq (2 * (length e + length d1 + length f) + 2) (2 * length d2) ++ var_seq (2 * length e) 2) ≤ Subst_tm (iter (2 * length e + 2) Up_tm_tm (t_subst enc R F W0 (d1 ++ f ++ d2))) (ren_tm (ren_shift d1 d2 e f) t) : Base.
Proof.
  intros enc_val H. rewrite ren_shift_shifted_t_subst_char. rewrite <- ren_var.
  erewrite <- (closed_ren (enc (d1 ++ e ++ d2))) with (f := ren_shift d1 d2 e f).
  2: { eapply typed_empty_closed. eapply enc_typed. eauto. }
  rewrite <- ren_apply_args. eapply ineq_ren. 1: apply ren_shift_well_typed'.
  easy.
Qed.

Lemma context_existence (s: tm) (d1 e f d2: str):
  nil ⊢ s: T e ⟶ T f -> exists C, ctxt_typed C (base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2)) Base (base_context (2 * length (d1 ++ f ++ d2) + 2)) Base /\ forall t, fill C t = apply_args (app s (lam_T (2 * length e + 2) t)) (var_seq (2 * length d1) (2 * length f) ++ var_seq (2 * length (d1 ++ f ++ d2)) 2).
Proof.
Admitted.

Lemma shift_t_subst_well_typed (enc: str -> tm) (R: list (str * str)) (F: str * str -> tm) (W0 d1 e f d2: str):
  subst_well_typed (base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2)) (base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2) ++ ((make_rule_types R)) ++ (cons (T W0) nil)) (iter (2 * length e + 2) Up_tm_tm (t_subst enc R F W0 (d1 ++ f ++ d2))).
Proof.
Admitted.

Lemma reduces_SR_SATIS_forward (E: list (val_word_enc)) (R: list (str * str)):
  forall p, SR R p -> SATIS E R p.
Proof.
  intros [W0 W] H.
  induction H as [W0 | W0 W W'].
  - unfold SATIS. exists (apply_args (var_tm (2 * length W0 + length R + 2)) (var_seq 0 (2 * length W0 + 2))).
    split.
    + eapply apply_args_typed with (l1 := base_context (2 * length W0 +2)).
      * rewrite <- T_list_to_fun. constructor. rewrite nth_error_app2.
        2: rewrite length_base_context; lia. rewrite nth_error_app2.
        2: rewrite length_base_context, make_rule_types_length; lia.
        rewrite length_base_context, make_rule_types_length.
        assert (eq1: 2 * length W0 + length R + 2 - (2 * length W0 + 2) - length R = 0) by lia.
        now rewrite eq1.
      * now rewrite length_base_context, length_var_seq.
      * intros n A t H1 H2. assert (n < 2 * length W0 + 2).
        { rewrite <- length_base_context. apply nth_error_Some. intros ?. now destruct (nth_error (base_context (2 * length W0 + 2)) n). }
        rewrite var_seq_nth in H2. 2: easy.
        injection H2. intros ?. subst. constructor. rewrite nth_error_app1. 2: now rewrite length_base_context.
        easy.
    + rewrite Forall_forall. intros [[enc v] enc_val] H f f_enc. cbn in *.
      assert (eq1: apply_args (var_tm (2 * length W0 + length R + 2)) (var_seq 0 (2 * length W0 + 2)) = fold_right (fun arg t : tm => app t arg) (var_tm (length W0 + (length W0 + 0) + length R + 2)) (rev (map (fun n : nat => var_tm n) (seq 0 (length W0 + (length W0 + 0) + 2))))) by easy.
      rewrite <- eq1. unfold satisfies. rewrite subst_apply_args.
      rewrite t_subst_map_var_id. asimpl. rewrite t_subst_initial.
      eapply obs_preorder_refl. eapply apply_args_typed with (l1 := base_context (2 * length W0 +2)).
      * rewrite <- T_list_to_fun. assert (eq2: base_context (2 * length W0 + 2) = nil ++ base_context (2 * length W0 + 2)) by easy.
        rewrite eq2. apply type_weakening. eapply enc_typed; eauto.
      * now rewrite length_base_context, length_var_seq.
      * intros n A t H1 H2. assert (n < 2 * length W0 + 2).
        { rewrite <- length_base_context. apply nth_error_Some. intros ?. now destruct (nth_error (base_context (2 * length W0 + 2)) n). }
        rewrite var_seq_nth in H2. 2: easy.
        injection H2. intros ?. subst. now constructor.
  - destruct IHrewt as [t [typed_t Ht]].
    inversion H0 as [d1 d2 e f]. destruct (In_nth_error _ _ H1) as [n Hn].
    exists (apply_args (app (var_tm (2 * length W' + 2 + n)) (lam_T (2 * length e + 2) (ren_tm (ren_shift d1 d2 e f) t))) (var_seq (2 * length d1) (2 * length f) ++ var_seq (2 * length W') 2)).
    split.
    + apply apply_args_typed with (l1 := base_context (2 * length f + 2)).
      * apply app_typed with (typ1 := T e).
        -- rewrite <- T_list_to_fun. constructor. rewrite nth_error_app2. 2: rewrite length_base_context, H3; lia.
           rewrite length_base_context, H3. assert (eq1: 2 * length W' + 2 + n - (2 * length W' + 2) = n) by lia.
           rewrite eq1. unfold make_rule_types. rewrite nth_error_app1.
           2: { rewrite map_length. apply nth_error_Some; intros H'. destruct (nth_error R n) eqn: eq2; discriminate. }
           rewrite nth_error_map. now rewrite Hn.
        -- rewrite (T_list_to_fun e). rewrite <- (base_context_rev (2 * length e + 2)).
           rewrite <- (length_base_context (2 * length e + 2)) at 1. eapply lam_list_to_type'.
           apply ren_shift_typed. now rewrite H2.
      * now rewrite length_base_context, app_length, length_var_seq, length_var_seq.
      * intros n' A' t' H1' H2'. erewrite base_context_some_base; eauto.
        assert (e1: n' < 2 * length f + 2).
        { rewrite <- length_base_context. erewrite <- nth_error_Some. intros ?. now destruct (nth_error (base_context (2 * length f + 2)) n'). }
        destruct (nat_ord_dec (2 * length f) n').
        -- rewrite nth_error_app2 in H2'. 2: now rewrite length_var_seq.
           rewrite length_var_seq in H2'. assert (eq3: n' - 2 * length f < 2) by lia.
           rewrite var_seq_nth in H2'. 2: easy.
           injection H2'. intros eq. fold (2 * length W') in eq. fold (2 * length f) in eq.
           subst. constructor. rewrite nth_error_app1.
           2: rewrite length_base_context; lia. apply base_context_le_base. lia.
        -- rewrite nth_error_app1 in H2'. 2: now rewrite length_var_seq.
           rewrite var_seq_nth in H2'. 2: easy. injection H2'. intros eq.
           fold (2 * length d1) in eq. subst. constructor.
           rewrite nth_error_app1. 2: rewrite length_base_context; simpl_list; lia.
           rewrite base_context_le_base. 1: easy. simpl_list. lia.
    + rewrite Forall_forall. intros enc H' F HF.
      rewrite Forall_forall in Ht. specialize (Ht enc H' F HF).
      destruct enc as [[enc v] enc_val].
      assert (eq1: snd (proj1_sig (exist (fun p : (str -> tm) * bool => word_encoding (fst p) (snd p)) (enc, v) enc_val)) = v) by easy.
      assert (eq2: fst (proj1_sig (exist (fun p : (str -> tm) * bool => word_encoding (fst p) (snd p)) (enc, v) enc_val)) = enc) by easy.
      rewrite eq1, eq2 in *.
      unfold satisfies in *. rewrite subst_apply_args. asimpl.
      rewrite <- H3. rewrite t_subst_map_var_id''. erewrite t_subst_rule; eauto.
      unfold fun_rule_encoding in HF. rewrite Forall_forall in HF.
      destruct (context_existence (F(e, f)) d1 e f d2) as [C [HC1 HC2]].
      1: now apply HF.
      rewrite lam_T_shift_subst. rewrite <- HC2.
      eapply obs_preorder_trans.
      5: {eapply obs_preorder_context. 3: exact HC1.
      3: { eapply derived_satis_ineq. 1: eauto. now rewrite H2. }
      - eapply apply_args_typed with (l1 := base_context (2 * length (d1 ++ e ++ d2) + 2)).
        + rewrite <- T_list_to_fun. assert (eq3: base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2) = nil ++ base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2)) by easy.
          rewrite eq3. apply type_weakening. eapply enc_typed. eauto.
        + rewrite length_base_context. repeat rewrite app_length. repeat rewrite length_var_seq. lia.
        + intros n' A' t' H1' H2'. assert (eq3: n' < 2 * length (d1 ++ e ++ d2) + 2).
        { rewrite <- length_base_context. apply nth_error_Some. intros ?. now destruct (nth_error (base_context (2 * length (d1 ++ e ++ d2) + 2)) n'). }
        erewrite base_context_some_base. 2: eauto.
        destruct (nat_ord_dec (2 * length d1) n').
        2: { rewrite nth_error_app1 in H2'. 2: rewrite length_var_seq; lia. rewrite var_seq_nth in H2'. 2: easy.
             injection H2'. intros ?. subst. constructor. rewrite nth_error_app2. 2: rewrite length_base_context; lia. apply base_context_le_base. rewrite length_base_context. simpl_list. lia. }
        destruct (nat_ord_dec (2 * (length d1 + length e)) n').
        2: { rewrite nth_error_app2 in H2'. 2: rewrite length_var_seq; lia. rewrite nth_error_app1 in H2'. 2: repeat rewrite length_var_seq; lia. rewrite var_seq_nth in H2'. 2: rewrite length_var_seq; lia.
             injection H2'. intros ?; subst. constructor. rewrite nth_error_app1. 2: rewrite length_var_seq, length_base_context; lia. apply base_context_le_base. rewrite length_var_seq; lia. }
        destruct (nat_ord_dec (2 * (length d1 + length e + length d2)) n').
        2: { rewrite nth_error_app2 in H2'. 2: rewrite length_var_seq; lia. rewrite nth_error_app2 in H2'. 2: repeat rewrite length_var_seq; lia. rewrite nth_error_app1 in H2'. 2: repeat rewrite length_var_seq; lia. rewrite var_seq_nth in H2'. 2: repeat rewrite length_var_seq; lia.
        repeat rewrite length_var_seq in H2'. destruct (arith_technical' (2 * (length d1 + length e)) n' (2 * length d2)) as [m [eqm Hm]].
        1, 2: lia. rewrite Hm in H2'. assert (eq4: 2 * (length e + length d1 + length f) + 2 + (2 * (length d1 + length e) + m - 2 * length d1 - 2 * length e) = 2 * (length e + length d1 + length f) + 2 + m) by lia.
        rewrite eq4 in H2'.
        injection H2'. intros ?; subst. constructor. rewrite nth_error_app2. 2: rewrite length_base_context; lia. apply base_context_le_base. rewrite length_base_context. simpl_list. lia. }
        rewrite nth_error_app2 in H2'. 2: rewrite length_var_seq; lia. rewrite nth_error_app2 in H2'. 2: repeat rewrite length_var_seq; lia. rewrite nth_error_app2 in H2'. 2: repeat rewrite length_var_seq; lia.
        repeat rewrite app_length in eq3. rewrite var_seq_nth in H2'.
        2: repeat rewrite length_var_seq; lia.
        injection H2'. intros ?; subst. constructor. repeat rewrite length_var_seq. rewrite nth_error_app1. 2: rewrite length_base_context; lia. apply base_context_le_base. lia.
        - eapply subst_preserves. 1: apply shift_t_subst_well_typed.
          eapply ren_preserves'. 1: apply ren_shift_well_typed. now rewrite H2.
   }
      * eapply apply_args_typed with (l1 := (base_context (2 * length (d1 ++ f ++ d2) + 2))).
        -- rewrite <- T_list_to_fun. assert (eq: base_context (2 * length (d1 ++ f ++ d2) + 2) = nil ++ base_context (2 * length (d1 ++ f ++ d2) + 2)) by now simpl_list.
           rewrite eq. eapply type_weakening. eapply enc_typed. eauto.
        -- now rewrite length_base_context, length_var_seq.
        -- intros n' A' t' H1' H2'. erewrite base_context_some_base. 2: eauto.
           assert (H3': n' < 2 * length (d1 ++ f ++ d2) + 2).
           { rewrite <- length_base_context. apply nth_error_Some. intros ?. now destruct (nth_error (base_context (2 * length (d1 ++ f ++ d2) + 2)) n'). }
           erewrite var_seq_nth in H2'. 2: easy.
           injection H2'. intros ?; subst. constructor. now apply base_context_le_base.
      * eapply fill_type. 2: exact HC1. eapply apply_args_typed with (l1 := base_context (2 * length (d1 ++ e ++ d2) + 2)).
        -- assert (eq3: base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2) = nil ++ base_context (2 * length e + 2) ++ base_context (2 * length (d1 ++ f ++ d2) + 2)) by easy.
           rewrite eq3. apply type_weakening. rewrite <- T_list_to_fun. eapply enc_typed. eauto.
        -- simpl_list. repeat rewrite length_var_seq; rewrite length_base_context. lia.
        -- intros n' A' t' H1' H2'. assert (eq3: n' < 2 * length (d1 ++ e ++ d2) + 2).
           { rewrite <- length_base_context. apply nth_error_Some. intros ?. now destruct (nth_error (base_context (2 * length (d1 ++ e ++ d2) + 2)) n'). }
           erewrite base_context_some_base. 2: eauto.
           destruct (nat_ord_dec (2 * length d1) n').
           2: { rewrite nth_error_app1 in H2'. 2: rewrite length_var_seq; lia. rewrite var_seq_nth in H2'. 2: easy.
                injection H2'. intros ?. subst. constructor. rewrite nth_error_app2. 2: rewrite length_base_context; lia. apply base_context_le_base. rewrite length_base_context. simpl_list. lia. }
           destruct (nat_ord_dec (2 * (length d1 + length e)) n').
           2: { rewrite nth_error_app2 in H2'. 2: rewrite length_var_seq; lia. rewrite nth_error_app1 in H2'. 2: repeat rewrite length_var_seq; lia. rewrite var_seq_nth in H2'. 2: rewrite length_var_seq; lia.
                injection H2'. intros ?; subst. constructor. rewrite nth_error_app1. 2: rewrite length_var_seq, length_base_context; lia. apply base_context_le_base. rewrite length_var_seq; lia. }
           destruct (nat_ord_dec (2 * (length d1 + length e + length d2)) n').
           2: { rewrite nth_error_app2 in H2'. 2: rewrite length_var_seq; lia. rewrite nth_error_app2 in H2'. 2: repeat rewrite length_var_seq; lia. rewrite nth_error_app1 in H2'. 2: repeat rewrite length_var_seq; lia. rewrite var_seq_nth in H2'. 2: repeat rewrite length_var_seq; lia.
           repeat rewrite length_var_seq in H2'. destruct (arith_technical' (2 * (length d1 + length e)) n' (2 * length d2)) as [m [eqm Hm]].
           1, 2: lia. rewrite Hm in H2'. assert (eq4: 2 * (length e + length d1 + length f) + 2 + (2 * (length d1 + length e) + m - 2 * length d1 - 2 * length e) = 2 * (length e + length d1 + length f) + 2 + m) by lia.
           rewrite eq4 in H2'.
           injection H2'. intros ?; subst. constructor. rewrite nth_error_app2. 2: rewrite length_base_context; lia. apply base_context_le_base. rewrite length_base_context. simpl_list. lia. }
           rewrite nth_error_app2 in H2'. 2: rewrite length_var_seq; lia. rewrite nth_error_app2 in H2'. 2: repeat rewrite length_var_seq; lia. rewrite nth_error_app2 in H2'. 2: repeat rewrite length_var_seq; lia.
           repeat rewrite app_length in eq3. rewrite var_seq_nth in H2'.
           2: repeat rewrite length_var_seq; lia.
           injection H2'. intros ?; subst. constructor. repeat rewrite length_var_seq. rewrite nth_error_app1. 2: rewrite length_base_context; lia. apply base_context_le_base. lia.
      * eapply fill_type. 2: exact HC1. eapply subst_preserves. 2: { eapply ren_preserves'. 2: eauto. rewrite <- H2. apply ren_shift_well_typed. }
        apply shift_t_subst_well_typed.
      * rewrite HC2. destruct (HF (e, f) H1) as [type_F [HF' _]].
        specialize (HF' d1 d2). cbn in HF'. unfold rule_ineq in HF'. exact HF'.
Qed.