Require Import List.
Export ListNotations.
Require Import Lia.
Require Import PeanoNat Arith.

Require Import KS_calc.
Require Import KS_dec.
Require Import KS_termination_measure.
Require Export KS_termination_prelims.
Require Export KS_termination_init.
Require Export KS_termination_ImpR.
Require Export KS_termination_ImpL.
Require Export KS_termination_KR.

(* Now that we have the list of all premises of a sequent via all rules, we can combine
   them all to obtain the list of all potential premises via the KS calculus. *)


Lemma finite_premises_of_S : forall (s : Seq), existsT2 listprems,
              (forall prems, ((KS_rules prems s) -> (InT prems listprems)) *
                             ((InT prems listprems) -> (KS_rules prems s))).
Proof.
intro s.
destruct (dec_KS_rules s).
- exists []. intros. split. intro. exfalso. apply f. exists prems. assumption.
  intro. inversion H.
- pose (finite_IdP_premises_of_S s). destruct s1.
  pose (finite_BotL_premises_of_S s). destruct s1.
  pose (finite_ImpR_premises_of_S s). destruct s1.
  pose (finite_ImpL_premises_of_S s). destruct s1.
  pose (finite_KR_premises_of_S s). destruct s1.
  exists (x ++ x0 ++ x1 ++ x2 ++ x3).
  split.
  * intro RA. inversion RA.
    { inversion H. subst. pose (p []). destruct p4. apply InT_or_app. auto. }
    { inversion H. subst. pose (p0 []). destruct p4. apply InT_or_app. right. apply InT_or_app. auto. }
    { inversion H. subst. pose (p1 [(Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)]). destruct p4.
      apply InT_or_app. right. apply InT_or_app. right. apply InT_or_app. left. auto. }
    { inversion H. subst. pose (p2 [(Γ0 ++ Γ1, Δ0 ++ A :: Δ1); (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)]).
      destruct p4. apply InT_or_app. right. apply InT_or_app. right. apply InT_or_app. right.
      apply InT_or_app. left. auto. }
    { inversion X. subst. pose (p3 [(unboxed_list , [A])]).
      destruct p4. apply InT_or_app. right. apply InT_or_app. right. apply InT_or_app.
      right. apply InT_or_app. auto. }
  * intro. apply InT_app_or in H. destruct H.
    { apply p in i. apply IdP ; try intro ; try apply f ; try auto ; try assumption. }
    { apply InT_app_or in i. destruct i.
      - apply p0 in i. apply BotL ; try intro ; try apply f ; try auto ; try assumption.
      - apply InT_app_or in i. destruct i.
        + apply p1 in i. apply ImpR ; try intro ; try apply f ; try auto ; try assumption.
        + apply InT_app_or in i. destruct i.
          * apply p2 in i. apply ImpL ; try intro ; try apply f ; try auto ; try assumption.
          * apply p3 in i. apply KR ; try intro ; try apply f ; try auto ; try assumption. }
Qed.

(* The next definitions "flattens" a list of lists of premises to a list of premises.*)

Definition list_of_premises (s : Seq) : list Seq :=
         flatten_list (proj1_sigT2 (finite_premises_of_S s)).

Lemma InT_list_of_premises_exists_prems : forall s prem, InT prem (list_of_premises s) ->
            existsT2 prems, (InT prem prems) * (KS_rules prems s).
Proof.
intros s prem X. unfold list_of_premises in X.
apply InT_flatten_list_InT_elem in X. destruct X. destruct p.
exists x. split. auto.
destruct (finite_premises_of_S s). pose (p x). destruct p0. apply k. assumption.
Qed.

Lemma exists_prems_InT_list_of_premises : forall s prem,
            (existsT2 prems, (InT prem prems) * (KS_rules prems s)) ->
            InT prem (list_of_premises s).
Proof.
intros. destruct X. destruct p. unfold list_of_premises. destruct (finite_premises_of_S s).
pose (p x). destruct p0. apply InT_trans_flatten_list with (bs:=x). assumption. simpl. apply i0.
assumption.
Qed.

Lemma find_the_max_mhd : forall concl l
      (Prem_mhd : forall prems : list Seq, KS_rules prems concl ->
                  forall prem : Seq, InT prem prems ->
                  existsT2 Dprem : derrec KS_rules (fun _ : Seq => True) prem,
                  is_mhd Dprem)
      (H1 : forall prem : Seq, InT prem l -> InT prem (list_of_premises concl))
      (H2 : forall (prem : Seq) (J : InT prem l), InT prem (proj1_sigT2
            (InT_list_of_premises_exists_prems concl _ (H1 prem J))))
      (H3 : forall (prem : Seq) (J : InT prem l), KS_rules (proj1_sigT2
            (InT_list_of_premises_exists_prems concl _ (H1 prem J))) concl)
      (NotNil: l <> nil),

existsT2 prem, existsT2 (J0: InT prem l), forall prem' (J1: InT prem' l),
       (derrec_height (proj1_sigT2 (Prem_mhd
        (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem' J1)))
        (H3 prem' J1)
        prem'
        (H2 prem' J1))))
       <=
       (derrec_height (proj1_sigT2 (Prem_mhd
        (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem J0)))
        (H3 prem J0)
        prem
        (H2 prem J0)))).
Proof.
induction l ; intros.
- exfalso. apply NotNil. reflexivity.
- clear NotNil. destruct l as [ | r l].
  * exists a. assert (InT a [a]). apply InT_eq. exists H. intros. inversion J1. subst.
    destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem' H))) (H3 prem' H) prem' (H2 prem' H)).
    destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem' J1))) (H3 prem' J1) prem' (H2 prem' J1)).
    simpl. auto. inversion H4.
  * assert (H1' : forall prem : Seq, InT prem (r :: l) -> InT prem (list_of_premises concl)).
    { intros. apply H1. apply InT_cons. assumption. }
    assert (Prem_mhd' : forall prems : list Seq, KS_rules prems concl -> forall prem : Seq,
                        InT prem prems -> existsT2 Dprem : derrec KS_rules (fun _ : Seq => True)
                        prem, is_mhd Dprem).
    { intros. apply Prem_mhd with (prems:= prems) ; try assumption. }
    assert (H2' : forall (prem : Seq) (J : InT prem (r :: l)), InT prem (proj1_sigT2
                  (InT_list_of_premises_exists_prems concl _ (H1' prem J)))).
    { intros. assert (InT prem (a :: r :: l)). apply InT_cons. assumption. pose (H2 _ H).
      destruct (InT_list_of_premises_exists_prems concl _ (H1' prem J)).
      simpl. destruct p. assumption. }
    assert (H3' : forall (prem : Seq) (J : InT prem (r :: l)), KS_rules
                (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1' prem J))) concl).
    { intros. destruct (InT_list_of_premises_exists_prems concl _ (H1' prem J)). simpl. destruct p.
      assumption. }
    assert (r :: l <> []). intro. inversion H.
    pose (IHl Prem_mhd' H1' H2' H3' H). destruct s. destruct s.
    (* I have a max in r :: l: so I simply need to compare it with a. *)
    assert (J2: InT a (a :: r :: l)). apply InT_eq.
    assert (J3: InT x (a :: r :: l)). apply InT_cons. assumption.
    (* The next assert decides on le between mhd of a and mhd of x. *)
    pose (le_dec
      (derrec_height (proj1_sigT2 (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 a J2)))
      (H3 a J2) a (H2 a J2))))
      (derrec_height
       (proj1_sigT2
          (Prem_mhd' (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1' x x0)))
             (H3' x x0) x (H2' x x0))))).
    destruct s.
    + exists x. exists J3. intros. inversion J1. subst.
      destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem' J1)))
      (H3 prem' J1) prem' (H2 prem' J1)). simpl.
      destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem' J2)))
      (H3 prem' J2) prem' (H2 prem' J2)). simpl in l1. unfold is_mhd in i0.
      pose (i0 x1).
      destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 x J3))) (H3 x J3) x (H2 x J3)).
      simpl.
      destruct (Prem_mhd' (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1' x x0)))
      (H3' x x0) x (H2' x x0)). simpl in l1.
      unfold is_mhd in i1. pose (i1 x4). lia.
      destruct (Prem_mhd' (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1' x x0)))
      (H3' x x0) x (H2' x x0)). simpl in l0.
      destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 x J3))) (H3 x J3) x (H2 x J3)).
      simpl.
      destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem' J1)))). simpl.
      assert (derrec_height
     (proj1_sigT2
        (Prem_mhd' (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1' prem' H4)))
           (H3' prem' H4) prem' (H2' prem' H4))) <= derrec_height x1).
      apply (l0 prem' H4). subst.
      unfold is_mhd in i0. pose (i0 x1).
      destruct (Prem_mhd' (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1' prem' H4)))
           (H3' prem' H4) prem' (H2' prem' H4)). simpl in H6. unfold is_mhd in i2. pose (i2 x3). lia.
    + exists a. exists J2. intros.
      inversion J1.
      { subst. destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem' J1)))
        (H3 prem' J1) prem' (H2 prem' J1)). simpl.
        destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem' J2)))
        (H3 prem' J2) prem' (H2 prem' J2)).
        simpl. unfold is_mhd in i0. pose (i0 x1). lia. }
      { subst.
        destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 a J2))) (H3 a J2) a (H2 a J2)).
        simpl.
        destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem' J1)))
        (H3 prem' J1) prem' (H2 prem' J1)). simpl.
        destruct (Prem_mhd' (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1' x x0)))
             (H3' x x0) x (H2' x x0)). simpl in l0.
        assert (derrec_height
       (proj1_sigT2
          (Prem_mhd' (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1' prem' H4)))
             (H3' prem' H4) prem' (H2' prem' H4))) <= derrec_height x3). apply l0.
       destruct (Prem_mhd' (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1' prem' H4)))
             (H3' prem' H4) prem' (H2' prem' H4)). simpl in H0. unfold is_mhd in i2.
       pose (i2 x2). simpl in *. lia. }
Qed.

Lemma term_IH_help : forall concl,
     (existsT2 prems, (KS_rules prems concl) * (prems <> [])) ->
     (forall prems, KS_rules prems concl -> (forall prem, InT prem prems -> (existsT2 Dprem, @is_mhd prem Dprem)))
      ->
     (existsT2 Maxprems Maxprem DMaxprem, (KS_rules Maxprems concl) * (@is_mhd Maxprem DMaxprem) * (InT Maxprem Maxprems) *
        (forall prems prem (Dprem : KS_drv prem), KS_rules prems concl -> InT prem prems ->
            derrec_height Dprem <= derrec_height DMaxprem)).
Proof.
intros concl FAH Prem_mhd.
pose (list_of_premises concl).
assert (H1: forall prem, InT prem l -> InT prem (list_of_premises concl)).
intros. auto.
assert (H2: forall prem (J: InT prem l), InT prem (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem J)))).
intros. destruct (InT_list_of_premises_exists_prems concl _ (H1 prem J)). destruct p. auto.
assert (H3: forall prem (J: InT prem l),
KS_rules (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem J))) concl).
intros. destruct (InT_list_of_premises_exists_prems concl _ (H1 prem J)). destruct p. auto.
assert (H4: forall prem (J: InT prem l), is_mhd (proj1_sigT2 (Prem_mhd
        (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem J)))
        (H3 prem J)
        prem
        (H2 prem J)))).
intros. intro. destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem J)))
(H3 prem J) prem (H2 prem J)). auto.
assert (l <> []). intro. destruct FAH. destruct p. destruct k.
- inversion i. subst. auto.
- inversion b. subst. auto.
- inversion i. subst. pose (@exists_prems_InT_list_of_premises (Γ0 ++ Γ1, Δ0 ++ A --> B :: Δ1) (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)).
  assert (InT (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1) (list_of_premises (Γ0 ++ Γ1, Δ0 ++ A --> B :: Δ1))). apply i0.
  exists [(Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)]. split. apply InT_eq. apply ImpR ; assumption.
  assert (InT (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1) l). auto. rewrite H in H5. inversion H5.
- inversion i. subst. pose (@exists_prems_InT_list_of_premises (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1) (Γ0 ++ Γ1, Δ0 ++ A :: Δ1)).
  assert (InT (Γ0 ++ Γ1, Δ0 ++ A :: Δ1) (list_of_premises (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1))). apply i0.
  exists [(Γ0 ++ Γ1, Δ0 ++ A :: Δ1); (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)]. split. apply InT_eq. apply ImpL ; assumption.
  assert (InT (Γ0 ++ Γ1, Δ0 ++ A :: Δ1) l). auto. rewrite H in H5. inversion H5.
- inversion k. subst. pose (@exists_prems_InT_list_of_premises (Γ0, Δ0 ++ Box A :: Δ1) (unboxed_list , [A])).
  assert (InT (unboxed_list , [A]) (list_of_premises (Γ0, Δ0 ++ Box A :: Δ1))). apply i.
  exists [(unboxed_list , [A])]. split. apply InT_eq. apply KR ; assumption.
  assert (InT (unboxed_list , [A]) l). auto. rewrite H in H6. inversion H6.

- pose (find_the_max_mhd _ _ Prem_mhd H1 H2 H3 H).
  destruct s. destruct s. exists (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 x x0))).
  exists x. exists (proj1_sigT2 (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 x x0)))
  (H3 x x0) x (H2 x x0))). repeat split ; try apply H3 ; try apply H4 ; try apply H2.
  intros prems prem Dprem RA IsPrem.
  assert (J3: InT prem l).
  pose (@exists_prems_InT_list_of_premises concl prem). apply i. exists prems. auto.
  assert (E1: derrec_height Dprem <= derrec_height (proj1_sigT2 (Prem_mhd (proj1_sigT2
  (InT_list_of_premises_exists_prems concl _ (H1 prem J3))) (H3 prem J3) prem (H2 prem J3)))).
  destruct (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 prem J3)))
  (H3 prem J3) prem (H2 prem J3)). auto.
  assert (E2: derrec_height (proj1_sigT2 (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl
  _ (H1 prem J3))) (H3 prem J3) prem (H2 prem J3))) <=
  derrec_height (proj1_sigT2 (Prem_mhd (proj1_sigT2 (InT_list_of_premises_exists_prems concl _ (H1 x x0)))
  (H3 x x0) x (H2 x x0)))). apply l0. lia.
Qed.

Lemma in_drs_concl_in_allT W rules prems ps (cn : W) (drs : dersrec rules prems ps)
  (dtn : derrec rules prems cn) : in_dersrec dtn drs -> InT cn ps.
Proof.
intro ind. induction ind. apply InT_eq.
apply InT_cons. assumption.
Qed.

Lemma dec_non_nil_prems: forall (concl : Seq), ((existsT2 prems, (KS_rules prems concl) * (prems <> []))) +
                                       ((existsT2 prems, (KS_rules prems concl) * (prems <> [])) -> False).
Proof.
intros. destruct (dec_KR_rule concl).
  + destruct s. left. exists [x]. split. apply KR in k ; auto. intro. inversion H.
  + destruct (dec_ImpR_rule concl).
    * destruct s. left. exists [x]. split. apply ImpR in i. assumption. intro. inversion H.
    * destruct (dec_ImpL_rule concl).
      { destruct s. destruct s. left. exists [x; x0]. split. apply ImpL in i. assumption.
        intro. inversion H. }
      { right. intro. destruct X. destruct p. inversion k.
        - subst. inversion H. auto.
        - subst. inversion H. auto.
        - subst. inversion H. subst. apply f0. exists (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1). assumption.
        - subst. inversion H. subst. apply f1. exists (Γ0 ++ Γ1, Δ0 ++ A :: Δ1).
          exists (Γ0 ++ B :: Γ1, Δ0 ++ Δ1). assumption.
        - subst. inversion X. subst. apply f. exists (unboxed_list , [A]). assumption. }
Qed.

(* The next theorem claims that every sequent s has a derivation DMax of maximal height. *)

Theorem KS_termin_base : forall n s, (n = measure s) ->
   existsT2 (DMax : KS_drv s), (@is_mhd s DMax).
Proof.
induction n as [n IH] using (well_founded_induction_type lt_wf).
assert (dersrecnil: dersrec KS_rules (fun _ => True) nil).
apply dersrec_nil.
intros. pose (dec_KS_rules s). destruct s0.
- assert (forall ps : list Seq, KS_rules ps s -> False).
  intros. apply f. exists ps. assumption. pose (@no_KS_rule_applic s H0).
  pose (dpI KS_rules (fun _ : Seq => True) s I).
  exists d. unfold is_mhd. intros. simpl. pose (e D1). rewrite e0. auto.
- assert (forall prems, KS_rules prems s -> (forall prem, InT prem prems ->
  (existsT2 Dprem, @is_mhd prem Dprem))).
  { simpl. intros prems X prem X0. inversion X.
    - inversion H0. subst. exfalso. inversion X0.
    - inversion H0. subst. exfalso. inversion X0.
    - inversion H0. subst. inversion X0. 2: inversion H1. subst.
      assert (J0: measure (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1) < measure (Γ0 ++ Γ1, Δ0 ++ A --> B :: Δ1)).
      unfold measure. simpl.
      repeat rewrite size_LF_dist_app. simpl. lia.
      pose (IH (measure (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)) J0 (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)).
      apply s. reflexivity.
    - inversion H0. subst. inversion X0 ; subst.
     + assert (J0: measure (Γ0 ++ Γ1, Δ0 ++ A :: Δ1) < measure (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1)).
        unfold measure. simpl. repeat rewrite size_LF_dist_app. simpl. lia.
        pose (IH (measure (Γ0 ++ Γ1, Δ0 ++ A :: Δ1)) J0 (Γ0 ++ Γ1, Δ0 ++ A :: Δ1)).
        apply s. reflexivity.
     + inversion H1. subst.
        assert (J0: measure (Γ0 ++ B :: Γ1, Δ0 ++ Δ1) < measure (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1)).
        unfold measure. simpl. repeat rewrite size_LF_dist_app. simpl. lia.
        pose (IH (measure (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)) J0 (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)).
        apply s. reflexivity. inversion H2.
    - inversion X1. subst. inversion X0. 2: inversion H0. subst.
      assert (J0: measure (unboxed_list , [A]) < measure (Γ0, Δ0 ++ Box A :: Δ1)).
      unfold measure. simpl. repeat rewrite size_LF_dist_app. simpl.
      pose (size_nobox_gen_ext _ _ X2). pose (size_unboxed ). lia.
      pose (IH (measure (unboxed_list , [A])) J0 (unboxed_list , [A])).
      apply s. reflexivity. }
    destruct (dec_non_nil_prems s).
    + pose (@term_IH_help s s1 X). repeat destruct s2. destruct p. destruct p. destruct p.
      inversion k.
      (* Use PIH and SIH here, depending on the rule applied *)
      * inversion H0. subst. inversion i.
      * inversion H0. subst. inversion i.
      * subst. inversion H0. subst. assert (E: x0 = (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)).
        inversion i. auto. inversion H1. subst.
        pose (dlCons x1 dersrecnil).
        pose (@derI _ _ (fun _ : Seq => True) [(Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)] (Γ0 ++ Γ1, Δ0 ++ A --> B :: Δ1) k d).
        exists d0. unfold is_mhd. intros. simpl. rewrite dersrec_height_nil with (ds:=dersrecnil). rewrite Nat.max_0_r.
        destruct D1.
        { simpl. lia. }
        { simpl.
          assert (forall p (d : derrec KS_rules (fun _ : Seq => True) p),
          in_dersrec d d1 -> derrec_height d <= (derrec_height x1)). intros.
          pose (@in_drs_concl_in_allT _ _ _ _ _ _ _ X0). subst. pose (l ps p d2 k0 i1). assumption.
          pose (dersrec_height_le H). lia. }
        { reflexivity. }
      * subst. inversion H0. subst. inversion i.
        { subst. pose (dpI KS_rules (fun _ : Seq => True) (Γ0 ++ B :: Γ1, Δ0 ++ Δ1) I).
          pose (dlCons d dersrecnil). pose (dlCons x1 d0).
          pose (@derI _ _ (fun _ : Seq => True) [(Γ0 ++ Γ1, Δ0 ++ A :: Δ1); (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)]
          (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1) k d1).
          exists d2. unfold is_mhd. intros. simpl. rewrite dersrec_height_nil with (ds:=dersrecnil). rewrite Nat.max_0_r.
          destruct D1.
          { simpl. lia. }
          { simpl.
            assert (forall p (d : derrec KS_rules (fun _ : Seq => True) p),
            in_dersrec d d3 -> derrec_height d <= (derrec_height x1)). intros.
            pose (@in_drs_concl_in_allT _ _ _ _ _ _ _ X0). subst. pose (l ps p d4 k0 i1). assumption.
            pose (dersrec_height_le H). lia. }
          { reflexivity. } }
        { inversion H1. subst. pose (dpI KS_rules (fun _ : Seq => True) (Γ0 ++ Γ1, Δ0 ++ A :: Δ1) I).
          pose (dlCons x1 dersrecnil). pose (dlCons d d0).
          pose (@derI _ _ (fun _ : Seq => True) [(Γ0 ++ Γ1, Δ0 ++ A :: Δ1); (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)]
          (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1) k d1).
          exists d2. unfold is_mhd. intros. simpl. rewrite dersrec_height_nil with (ds:=dersrecnil). rewrite Nat.max_0_r.
          destruct D1.
          { simpl. lia. }
          { simpl.
            assert (forall p (d : derrec KS_rules (fun _ : Seq => True) p),
            in_dersrec d d3 -> derrec_height d <= (derrec_height x1)). intros.
            pose (@in_drs_concl_in_allT _ _ _ _ _ _ _ X0). subst. pose (l ps p d4 k0 i1). assumption.
            pose (dersrec_height_le H). lia. }
          { reflexivity. }
          inversion H4. }
      * subst. inversion X0. subst. assert (E: x0 = (unboxed_list , [A])).
        inversion i. subst. auto. inversion H1. subst.
        pose (dlCons x1 dersrecnil).
        pose (@derI _ _ (fun _ : Seq => True) [(unboxed_list , [A])] (Γ0, Δ0 ++ Box A :: Δ1) k d).
        exists d0. unfold is_mhd. intros. simpl. rewrite dersrec_height_nil with (ds:=dersrecnil). rewrite Nat.max_0_r.
        destruct D1.
        { simpl. lia. }
        { simpl.
          assert (forall p (d : derrec KS_rules (fun _ : Seq => True) p),
          in_dersrec d d1 -> derrec_height d <= (derrec_height x1)). intros.
          pose (@in_drs_concl_in_allT _ _ _ _ _ _ _ X2). subst. pose (l ps p d2 k0 i1). assumption.
          pose (dersrec_height_le H0). lia. }
        { reflexivity. }
    + destruct s0. inversion k.
      * inversion H0. subst. pose (@derI _ _ (fun _ : Seq => True) [] (Γ0 ++ # P :: Γ1, Δ0 ++ # P :: Δ1) k dersrecnil).
        exists d. unfold is_mhd. intros. simpl. rewrite dersrec_height_nil with (ds:=dersrecnil).
        destruct D1.
        { simpl. lia. }
        { simpl. destruct k0.
          - inversion i. subst. rewrite dersrec_height_nil with (ds:=d0). lia. reflexivity.
          - inversion b. subst. rewrite dersrec_height_nil with (ds:=d0). lia. reflexivity.
          - inversion i. subst. exfalso. apply f. exists [(Γ2 ++ A :: Γ3, Δ2 ++ B :: Δ3)]. split.
            apply ImpR in i. assumption. intro. inversion H.
          - inversion i. subst. exfalso. apply f. exists [(Γ2 ++ Γ3, Δ2 ++ A :: Δ3); (Γ2 ++ B :: Γ3, Δ2 ++ Δ3)]. split.
            apply ImpL in i. assumption. intro. inversion H.
          - inversion k0. subst. exfalso. apply f. exists [(unboxed_list , [A])]. split.
            apply KR in k0. assumption. intro. inversion H1. }
        { reflexivity. }
      * inversion H0. subst. pose (@derI _ _ (fun _ : Seq => True) [] (Γ0 ++ Bot :: Γ1, Δ) k dersrecnil).
        exists d. unfold is_mhd. intros. simpl. rewrite dersrec_height_nil with (ds:=dersrecnil).
        destruct D1.
        { simpl. lia. }
        { simpl. destruct k0.
          - inversion i. subst. rewrite dersrec_height_nil with (ds:=d0). lia. reflexivity.
          - inversion b. subst. rewrite dersrec_height_nil with (ds:=d0). lia. reflexivity.
          - inversion i. subst. exfalso. apply f. exists [(Γ2 ++ A :: Γ3, Δ0 ++ B :: Δ1)]. split.
            apply ImpR in i. assumption. intro. inversion H.
          - inversion i. subst. exfalso. apply f. exists [(Γ2 ++ Γ3, Δ0 ++ A :: Δ1); (Γ2 ++ B :: Γ3, Δ0 ++ Δ1)]. split.
            apply ImpL in i. assumption. intro. inversion H.
          - inversion k0. subst. exfalso. apply f. exists [(unboxed_list , [A])]. split.
            apply KR in k0. assumption. intro. inversion H1. }
        { reflexivity. }
      * inversion H0. subst. exfalso. apply f. exists [(Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)]. split. assumption. intro.
        inversion H.
      * inversion H0. subst. exfalso. apply f. exists [(Γ0 ++ Γ1, Δ0 ++ A :: Δ1); (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)]. split. assumption. intro.
        inversion H.
      * inversion X0. subst. exfalso. apply f. exists [(unboxed_list , [A])]. split. assumption. intro.
        inversion H.
Qed.

Theorem KS_termin : forall s, existsT2 (DMax : KS_drv s), (@is_mhd s DMax).
Proof.
intro s. pose (@KS_termin_base (measure s)). apply s0 ; reflexivity.
Qed.

Theorem KS_termin1 : forall (s : Seq), exists (DMax : KS_drv s), (is_mhd DMax).
Proof.
intro s.
assert (J1: measure s = measure s). reflexivity.
pose (@KS_termin_base (measure s) s J1).
destruct s0. exists x. assumption.
Qed.

Theorem KS_termin2 : forall s, exists (DMax : KS_drv s), (is_mhd DMax).
Proof.
intro s.
assert (J1: measure s = measure s). reflexivity.
pose (@KS_termin_base (measure s) s J1 ).
destruct s0. exists x. assumption.
Qed.

Theorem KS_termin3 : forall (s : Seq), existsT2 (DMax : KS_drv s), (is_mhd DMax).
Proof.
intro s. pose (@KS_termin_base (measure s)). apply s0 ; reflexivity.
Qed.

(* Now we can prove that the maximal height of derivations (mhd) for sequents
   decreases upwards in the applicability of the proofs. In other words, if a sequent s is the
   conclusion of an instance of a rule R of KS with premises in ps, then for any element s0 of
   ps we have that (mhd s0) < (mhd s).

   To do so we first define mhd.*)


Definition mhd (s: Seq) : nat := derrec_height (proj1_sigT2 (KS_termin s)).

Lemma KS_termin_der_is_mhd : forall s, (@is_mhd s (proj1_sigT2 (@KS_termin s))).
Proof.
intro s. destruct KS_termin. auto.
Qed.

Theorem RA_mhd_decreases : forall prems concl, (KS_rules prems concl) ->
                             (forall prem, (In prem prems) -> (mhd prem) < (mhd concl)).
Proof.
intros. inversion X.
- inversion H0. subst. inversion H.
- inversion H0. subst. inversion H.
- inversion H0. subst. inversion H.
  * subst. apply le_False_lt. intro.
    pose (d:= proj1_sigT2 (KS_termin (Γ0 ++ Γ1, Δ0 ++ A --> B :: Δ1))).
    pose (d0:=proj1_sigT2 (KS_termin (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1))).
    assert (dersrecnil: dersrec KS_rules (fun _ => True) nil).
    apply dersrec_nil. pose (dlCons d0 dersrecnil).
    pose (@derI _ _ (fun _ : Seq => True) [(Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)] (Γ0 ++ Γ1, Δ0 ++ A --> B :: Δ1) X d1).
    assert (E1: derrec_height d0 = mhd (Γ0 ++ A :: Γ1, Δ0 ++ B :: Δ1)).
    unfold mhd. auto.
    assert (E2: derrec_height d = mhd (Γ0 ++ Γ1, Δ0 ++ A --> B :: Δ1)).
    unfold mhd. auto.
    assert (@is_mhd (Γ0 ++ Γ1, Δ0 ++ A --> B :: Δ1) d). apply KS_termin_der_is_mhd.
    unfold is_mhd in H2. pose (H2 d2). simpl in l. rewrite dersrec_height_nil in l. rewrite Nat.max_0_r in l.
    lia. reflexivity.
  * inversion H1.
- inversion H0. subst. inversion H.
    * subst. apply le_False_lt. intro.
      pose (d:=proj1_sigT2 (KS_termin (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1))).
      pose (d0:=proj1_sigT2 (KS_termin (Γ0 ++ Γ1, Δ0 ++ A :: Δ1))).
      assert (dersrecnil: dersrec KS_rules (fun _ => True) nil).
      apply dersrec_nil.
      pose (dpI KS_rules (fun _ : Seq => True) (Γ0 ++ B :: Γ1, Δ0 ++ Δ1) I).
      pose (dlCons d1 dersrecnil). pose (dlCons d0 d2).
      pose (@derI _ _ (fun _ : Seq => True) [(Γ0 ++ Γ1, Δ0 ++ A :: Δ1); (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)]
      (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1) X d3).
      assert (E1: derrec_height d0 = mhd (Γ0 ++ Γ1, Δ0 ++ A :: Δ1)).
      unfold mhd. auto.
      assert (E2: derrec_height d = mhd (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1)).
      unfold mhd. auto.
      assert (@is_mhd (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1) d). apply KS_termin_der_is_mhd.
      unfold is_mhd in H2. pose (H2 d4). simpl in l. rewrite dersrec_height_nil in l. rewrite Nat.max_0_r in l.
      lia. reflexivity.
    * inversion H1. subst. apply le_False_lt. intro.
      pose (d:=proj1_sigT2 (KS_termin (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1))).
      pose (d0:=proj1_sigT2 (KS_termin (Γ0 ++ B :: Γ1, Δ0 ++ Δ1))).
      assert (dersrecnil: dersrec KS_rules (fun _ => True) nil).
      apply dersrec_nil.
      pose (dpI KS_rules (fun _ : Seq => True) (Γ0 ++ Γ1, Δ0 ++ A :: Δ1) I).
      pose (dlCons d0 dersrecnil). pose (dlCons d1 d2).
      pose (@derI _ _ (fun _ : Seq => True) [(Γ0 ++ Γ1, Δ0 ++ A :: Δ1); (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)]
      (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1) X d3).
      assert (E1: derrec_height d0 = mhd (Γ0 ++ B :: Γ1, Δ0 ++ Δ1)).
      unfold mhd. auto.
      assert (E2: derrec_height d = mhd (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1)).
      unfold mhd. auto.
      assert (@is_mhd (Γ0 ++ A --> B :: Γ1, Δ0 ++ Δ1) d). apply KS_termin_der_is_mhd.
      unfold is_mhd in H3. pose (H3 d4). simpl in l. rewrite dersrec_height_nil in l. rewrite Nat.max_0_r in l.
      simpl in l. lia. reflexivity. inversion H2.
- inversion X0. subst. inversion H.
  * subst. apply le_False_lt. intro.
    pose (d:=proj1_sigT2 (KS_termin (Γ0, Δ0 ++ Box A :: Δ1))).
    pose (d0:=proj1_sigT2 (KS_termin (unboxed_list , [A]))).
    assert (dersrecnil: dersrec KS_rules (fun _ => True) nil).
    apply dersrec_nil. pose (dlCons d0 dersrecnil).
    pose (@derI _ _ (fun _ : Seq => True) [(unboxed_list , [A])] (Γ0, Δ0 ++ Box A :: Δ1) X d1).
    assert (E1: derrec_height d0 = mhd (unboxed_list , [A])).
    unfold mhd. auto.
    assert (E2: derrec_height d = mhd (Γ0, Δ0 ++ Box A :: Δ1)).
    unfold mhd. auto.
    assert (@is_mhd (Γ0, Δ0 ++ Box A :: Δ1) d). apply KS_termin_der_is_mhd.
    unfold is_mhd in H1. pose (H1 d2). simpl in l. rewrite dersrec_height_nil in l. rewrite Nat.max_0_r in l.
    lia. reflexivity.
  * inversion H0.
Qed.