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

  Require Import general_export.

  Require Import GLS_export.

  Require Import UIGL_Def_measure.
  Require Import UIGL_Canopy.
  Require Import UIGL_irred_short.
  Require Import UIGL_braga.
  Require Import UIGL_LexSeq.
  Require Import UIGL_nodupseq.
  Require Import UIGL_And_Or_rules.
  Require Import UIGL_UI_prelims.
  Require Import UIGL_UI_inter.

Theorem gen_rec_UI_imp : forall s sr p X Y,
              GLS_prv (X, (list_conj (map (N p sr) (Canopy (nodupseq s)))) --> (UI p s) :: Y).
  Proof.
  pose (d:=LexSeq_ind (fun (s:Seq) => forall sr p X Y,
              GLS_prv (X, (list_conj (map (N p sr) (Canopy (nodupseq s)))) --> (UI p s) :: Y))).
  apply d. clear d. intros s IH.
  intros.
  destruct (empty_seq_dec s) as [EE | DE].
  (* s is the empty sequent. *)
  { subst ; simpl in *. unfold nodupseq ; simpl.
    assert ((Canopy ([], [])) = [([], [])]). apply Id_InT_Canopy ; auto.
    apply critical_Seq_InT_Canopy ; auto. apply critical_empty_seq. rewrite H ; simpl.
    assert (GUI p ([],[]) Bot). apply GUI_empty_seq ; auto. apply UI_GUI in H0.
    rewrite H0 in *. pose critical_empty_seq. pose not_init_empty_seq.
    unfold N. destruct (N_pwc p sr ([], [])). simpl.
    assert ((forall (x : Seq) (l m : MPropF), (fun (s1 : Seq) (A : MPropF) => UI p s1 = A) x l -> (fun (s1 : Seq) (A : MPropF) => UI p s1 = A) x m -> l = m)).
    intros. subst. auto.
    assert (J11: Gimap (GUI p) (GLR_prems (nodupseq ([],[]))) (map (UI p) (GLR_prems (nodupseq ([],[]))))).
    apply Gimap_map. intros. apply UI_GUI ; auto.
    assert (J41: Gimap (GUI p) (GLR_prems (nodupseq (nodupseq ([],[])))) (map (UI p) (GLR_prems (nodupseq (nodupseq ([],[])))))).
    apply Gimap_map. intros. apply UI_GUI ; auto.
    destruct (Compare_dec.lt_dec (length (usable_boxes ([],[]))) (length (usable_boxes sr))).
    + assert (J0: GUI p (nodupseq ([],[])) (UI p (nodupseq ([],[])))). apply UI_GUI ; auto. apply UI_GUI in J0.
       rewrite ub_nodupseq in l.
       epose (GN_inv_noinit_lessub p g f l (UI_spec p _)). rewrite <- e. rewrite H0.
       apply derI with (ps:=[([] ++ (And Bot Top) :: X, [] ++ Bot :: Y)]). apply ImpR. apply ImpRRule_I.
       apply dlCons. 2: apply dlNil. simpl.
       pose (AndL (X, Bot :: Y) Bot Top). simpl in g0. apply g0. clear g0.
       apply derI with []. 2: apply dlNil. apply BotL ; apply (BotLRule_I []).
    + rewrite ub_nodupseq in n. pose (GN_inv_noinit_nolessub _ g f n J41). rewrite <- e. clear e. simpl.
        unfold nodupseq in * ; simpl in *. destruct (GLR_prems ([], [])) eqn:E; simpl.
        { apply derI with (ps:=[([] ++ (And (Or (Or )) Top) :: X, [] ++ Bot :: Y)]). apply ImpR. apply ImpRRule_I.
          apply dlCons. 2: apply dlNil. simpl.
          pose (AndL (X, Bot :: Y) (Or (Or )) Top). simpl in g0. apply g0. clear g0.
          pose (OrL (Top :: X, :: Y)). simpl in g0. apply g0 ; clear g0.
          apply derI with []. 2: apply dlNil. apply BotL ; apply (BotLRule_I []).
          pose (OrL (Top :: X, :: Y)). simpl in g0. apply g0 ; clear g0.
          all: apply derI with [] ; [apply BotL ; apply (BotLRule_I []) | apply dlNil]. }
        { exfalso. unfold GLR_prems in E. destruct (finite_GLR_premises_of_S ([], [])).
           simpl in *. destruct x0. simpl in E. inversion E. simpl in E. pose (p0 l0).
           destruct p1. assert (InT l0 (l0 :: x0)). apply InT_eq. apply g0 in H2. inversion H2.
           destruct Δ0 ; subst ; inversion H6. } }
  (* s is not the empty sequent. *)
  { destruct (critical_Seq_dec s).
  (* If s is critical. *)
  - assert ((Canopy (nodupseq s)) = [nodupseq s]). apply Id_InT_Canopy ; auto.
    apply critical_Seq_InT_Canopy ; auto. apply critical_nodupseq in c ; auto.
    destruct (dec_init_rules s).
    (* If s is initial. *)
    * assert (is_init s). auto.
      assert (J0: GUI p s (UI p s)). apply UI_GUI ; auto.
      pose (@GUI_inv_critic_init p s (UI p s) J0 c X0). rewrite <- e. rewrite H. simpl. unfold N.
      destruct (N_pwc p sr (nodupseq s)).
      simpl. assert (is_init (nodupseq s)). pose (is_init_nodupseq s) ; destruct p0 ; auto. pose (GN_inv_init _ g).
      apply derI with (ps:=[([] ++ And x Top :: X, [] ++ Top :: Y)]).
      apply ImpR. apply ImpRRule_I. apply dlCons. 2: apply dlNil. simpl.
      epose (TopR _ []). simpl in g0 ; apply g0.
    (* If s is not initial. *)
    * rewrite H. simpl. unfold N.
      destruct (N_pwc p sr (nodupseq s)). simpl.
      assert (is_init s -> False). auto.
      assert (J40: is_init (nodupseq s) -> False). intro. apply H0. pose (is_init_nodupseq s) ; destruct p0 ; auto.
      assert ((forall (x : Seq) (l m : MPropF), (fun (s1 : Seq) (A : MPropF) => UI p s1 = A) x l -> (fun (s1 : Seq) (A : MPropF) => UI p s1 = A) x m -> l = m)).
      intros. subst. auto.
      assert (J11: Gimap (GUI p) (GLR_prems (nodupseq s)) (map (UI p) (GLR_prems (nodupseq s)))).
      apply Gimap_map. intros. apply UI_GUI ; auto.
      assert (J41: Gimap (GUI p) (GLR_prems (nodupseq (nodupseq s))) (map (UI p) (GLR_prems (nodupseq (nodupseq s))))).
      apply Gimap_map. intros. apply UI_GUI ; auto.
      destruct (Compare_dec.lt_dec (length (usable_boxes s)) (length (usable_boxes sr))).
      (* If s has less usable boxes than sr. *)
      + assert (J0: GUI p (nodupseq s) (UI p (nodupseq s))). apply UI_GUI ; auto. apply UI_GUI in J0.
         rewrite ub_nodupseq in l.
         epose (GN_inv_noinit_lessub p g J40 l (UI_spec p _)). rewrite <- e.
         apply derI with (ps:=[([] ++ (And (UI p (nodupseq s)) Top) :: X, [] ++ UI p s :: Y)]). apply ImpR. apply ImpRRule_I.
         apply dlCons. 2: apply dlNil. simpl.
         pose (AndL (X, UI p s :: Y) (UI p (nodupseq s)) Top). simpl in g0. apply g0. clear g0.
         apply UI_nodupseq.
      (* If s does not have less usable boxes than sr. *)
      + rewrite ub_nodupseq in n. pose (GN_inv_noinit_nolessub _ g J40 n J41). rewrite <- e. clear e.
          assert (J0: GUI p s (UI p s)). apply UI_GUI ; auto.
          assert (J1: Gimap (GUI p) (GLR_prems (nodupseq s)) (map (UI p) (GLR_prems (nodupseq s)))). apply Gimap_map. intros.
          apply UI_GUI ; auto.
          assert (J2: Gimap (GN p (GUI p) s) (Canopy (nodupseq (XBoxed_list (top_boxes (fst s)), []%list)))
          (map (N p s) (Canopy (nodupseq (XBoxed_list (top_boxes (fst s)), []%list))))).
          simpl. apply Gimap_map. intros. apply (N_spec p s x0).
          assert (J42: Gimap (GN p (GUI p) s) (Canopy (nodupseq (XBoxed_list (top_boxes (fst s)), []%list)))
          (map (N p s) (Canopy (nodupseq (XBoxed_list (top_boxes (fst s)), []%list))))).
          simpl. apply Gimap_map. intros. apply (N_spec p s x0).
          pose (@GUI_inv_critic_not_init p s _ _ _ J0 c DE H0 J1 J42). rewrite <- e. clear e. simpl.
          remember (Or (list_disj (restr_list_prop p (nodup eq_dec_form (snd s)))) (Or (list_disj (map Neg (restr_list_prop p (nodup eq_dec_form (fst s))))) (list_disj (map Box (map (UI p) (GLR_prems (nodupseq (nodupseq s)))))))) as disj1.
          remember (Or (list_disj (restr_list_prop p (snd s))) (Or (list_disj (map Neg (restr_list_prop p (fst s))))
          (Or (list_disj (map Box (map (UI p) (GLR_prems (nodupseq s))))) (Diam (list_conj (map (N p s) (Canopy (nodupseq (XBoxed_list (top_boxes (fst s)), []%list)))))))))
          as disj2.
          apply derI with (ps:=[([] ++ (And disj1 Top) :: X, [] ++ disj2 :: Y)]). apply ImpR. apply ImpRRule_I.
          apply dlCons. 2: apply dlNil. simpl.
          pose (AndL (X, disj2 :: Y) disj1 Top). simpl in g0. apply g0. clear g0.
          pose (@GLS_prv_list_wkn_L [disj1] [] [disj2]).
          pose (@GLS_prv_list_wkn_R (disj1 :: Top :: X) [disj2] []). simpl in g1. simpl in g0.
          assert (GLS_prv (disj1 :: Top :: X, [disj2])).
          assert (GLS_prv ([disj1], [disj2])).
          rewrite Heqdisj2.
          pose (OrR ([disj1],[])). simpl in g2. apply g2 ; clear g2. subst.
          epose (OrL (_,_)). simpl in g2. apply g2 ; clear g2.
          eapply (list_disj_L) with (s:=([],_)). intros. simpl.
          epose (list_disj_wkn_R _ ([A], _) A). simpl in g2. apply g2 ; clear g2. subst.
          apply restr_list_prop_nodup ; auto.
          epose (Id_all_form A [] [] []). simpl in d. apply d.
          epose (OrL (_,_)). simpl in g2. apply g2 ; clear g2.
          apply GLS_prv_wkn_R with
          (s:= ([list_disj (map Neg (restr_list_prop p (nodup eq_dec_form (fst s))))], [Or (list_disj (map Neg (restr_list_prop p (fst s))))
          (Or (list_disj (map Box (map (UI p) (GLR_prems ( nodupseq s)))))
          (Diam (list_conj (map (N p s) (Canopy (nodupseq (XBoxed_list (top_boxes (fst s)), []%list)))))))]))
          (A:=list_disj (restr_list_prop p (snd s))).
          2: epose (wkn_RI (list_disj (restr_list_prop p (snd s))) _ [] _) ; simpl in w ; apply w.
          epose (OrR (_,_)). simpl in g2. apply g2 ; clear g2.
          eapply (list_disj_L) with (s:=([],_)). intros. simpl.
          epose (list_disj_wkn_R _ ([A], _) A). simpl in g2. apply g2 ; clear g2. subst.
          apply InT_map_iff. apply InT_map_iff in H2. destruct H2. exists x0.
          destruct p0 ; split ; auto. apply restr_list_prop_nodup ; auto.
          epose (Id_all_form A [] [] []). simpl in d. apply d. clear g0. clear g1.
          apply GLS_prv_wkn_R with
          (s:= ([list_disj (map Box (map (UI p) (GLR_prems (nodupseq (nodupseq s)))))], [Or (list_disj (map Neg (restr_list_prop p (fst s))))
          (Or (list_disj (map Box (map (UI p) (GLR_prems ( nodupseq s)))))
          (Diam (list_conj (map (N p s) (Canopy (nodupseq (XBoxed_list (top_boxes (fst s)), []%list)))))))]))
          (A:=list_disj (restr_list_prop p (snd s))).
          2: epose (wkn_RI (list_disj (restr_list_prop p (snd s))) _ [] _) ; simpl in w ; apply w.
          epose (OrR (_,_)). simpl in g0. apply g0 ; clear g0.
          apply GLS_prv_wkn_R with
          (s:= ([list_disj (map Box (map (UI p) (GLR_prems (nodupseq (nodupseq s)))))], [(Or (list_disj (map Box (map (UI p) (GLR_prems (nodupseq s)))))
          (Diam (list_conj (map (N p s) (Canopy (nodupseq (XBoxed_list (top_boxes (fst s)), []%list)))))))]))
          (A:=list_disj (map Neg (restr_list_prop p (fst s)))).
          2: epose (wkn_RI (list_disj (map Neg (restr_list_prop p (fst s)))) _ [] _) ; simpl in w ; apply w.
          epose (OrR (_,_)). simpl in g0. apply g0 ; clear g0.
          eapply (list_disj_L) with (s:=([],_)). intros. simpl.
          apply InT_map_iff in H2. destruct H2. destruct p0 ; subst. apply InT_map_iff in i.
          destruct i. destruct p0 ; subst.
          apply nodupseq_GLR_prems in i. destruct i. destruct p0. destruct p0. subst.
          epose (list_disj_wkn_R _ (_,[_]) (Box (UI p x0))). apply g0 ; clear g0. apply InT_map_iff.
          exists (UI p x0). split ; auto. apply InT_map_iff. exists x0 ; auto.
          simpl. apply derI with (ps:=[([UI p x1;Box (UI p x1);Box (UI p x0)], [UI p x0])]).
          apply GLR. epose (@GLRRule_I _ [Box (UI p x1)] _ [] [_]). simpl in g0. apply g0.
          intros A H3. inversion H3 ; subst. eexists ; auto. inversion H2. apply univ_gen_ext_refl.
          apply dlCons. 2: apply dlNil. eapply (UI_nodupseq_gen x1 x0 p _ []).
          unfold nodupseq ; simpl. rewrite e. rewrite e0 ; auto.
          epose (g0 X0 (Top :: X)). rewrite app_nil_r in g2. apply g2.
          epose (g1 X0 Y). rewrite app_nil_r in g2. apply g2.
  (* If s is not critical. *)
  - assert (J0: GUI p s (UI p s)). apply UI_GUI ; auto.
    assert (J1: Gimap (GUI p) (Canopy (nodupseq s)) (map (UI p) (Canopy (nodupseq s)))). apply Gimap_map. intros.
    apply UI_GUI ; auto.
    pose (@GUI_inv_not_critic p s (UI p s) _ J0 f J1). rewrite <- e.
    apply derI with (ps:=[(list_conj (map (N p sr) (Canopy (nodupseq s))) :: X, list_conj (map (UI p) (Canopy (nodupseq s))) :: Y)]).
    apply ImpR. epose (ImpRRule_I _ _ [] _ []). simpl in i. apply i. apply dlCons. 2: apply dlNil.
    epose (list_conj_R _ (_,_)). simpl in g. apply g. clear g. intros. apply InT_map_iff in H. destruct H.
    destruct p0 ; subst.
    epose (list_conj_wkn_L _ (_,_) (N p sr x)). simpl in g. apply g ; clear g.
    apply InT_map_iff. exists x ; split ; auto.
    pose (Canopy_LexSeq _ _ i). destruct s0.
    exfalso. subst. apply Canopy_critical in i. apply critical_nodupseq in i ; auto.
    assert (J20: LexSeq x s). pose (LexSeq_nodupseq_case s). destruct s0. apply LexSeq_trans with (y:=nodupseq s) ; auto.
    destruct p0. unfold LexSeq ; unfold less_thanS ; unfold GLS_termination_measure.measure ; inversion l ; subst.
    rewrite <- e1 in H2. rewrite H2. apply DLW_wf_lex.lex_skip ; auto. rewrite <- e0 ; auto.
    apply DLW_wf_lex.lex_cons ; auto. rewrite e1 ; auto.
    pose (IH _ J20 sr p X Y). pose (Canopy_critical _ _ i).
    assert (Canopy (nodupseq x) = [nodupseq x]). apply Id_InT_Canopy ; auto.
    apply critical_Seq_InT_Canopy ; auto. rewrite <- critical_nodupseq ; auto.
    rewrite H in g. simpl in g.
    epose (ImpRRule_I _ _ [] _ [] _). simpl in i0.
    pose (@ImpR_inv _ (And (N p sr (nodupseq x)) Top :: X, UI p x :: Y) g i0).
    epose (TopL_remove _ [] (N p sr x :: X)). simpl in g1. apply g1. clear g1.
    epose (GLS_cut_adm (And (N p sr x) Top) [] (Top :: N p sr x :: X) [] (UI p x :: Y)).
    simpl in g1. apply g1 ; clear g1.
    epose (AndR (_,_)). simpl in g1. apply g1 ; clear g1.
    epose (Id_all_form (N p sr x) [Top] X []). simpl in d. apply d.
    epose (TopR _ []). simpl in g1 ; apply g1.
    pose (@GLS_prv_list_wkn_L [And (N p sr x) Top] X (UI p x :: Y)).
    simpl in g1. apply g1 with (l:=(Top :: [N p sr x])) ; clear g1.
    eapply AndL with (s:=(X,_)) ; simpl.
    eapply AndL_inv with (s:=(_,_)) in g0. simpl in g0.
    pose (N_nodupseq sr x p (Top :: X) (UI p x :: Y)).
    epose (GLS_cut_adm (N p sr (nodupseq x)) [] (N p sr x :: Top :: X) [] (UI p x :: Y)).
    simpl in g2. apply g2 ; clear g2 ; auto.
    apply GLS_prv_wkn_L with (s:= (N p sr (nodupseq x) :: Top :: X, UI p x :: Y))
    (A:=N p sr x) ; auto.
    epose (wkn_LI (N p sr x) [_] _ _) ; simpl in w ; apply w. }
  Qed.

  Theorem rec_UI_imp : forall s p X Y, (is_init s -> False) -> (critical_Seq s) ->
              GLS_prv (X,
        (list_conj (map (N p s) (Canopy (nodupseq (XBoxed_list (top_boxes (fst s)), []%list))))) -->
        (UI p (XBoxed_list (top_boxes (fst s)), []%list)) :: Y).
  Proof.
  intros. apply gen_rec_UI_imp.
  Qed.