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.

  (* Operation to remove and replace an element in a list. *)

  Definition replace (A B : MPropF) (l : list MPropF) : list MPropF :=
  let subst_form A B C := if (eq_dec_form A C) then B else C in
  map (subst_form A B) l.

  Lemma replace_app : forall l0 l1 A B, replace A B (l0 ++ l1) = replace A B l0 ++ replace A B l1.
  Proof. intros. apply map_app. Qed.

  Lemma in_not_touched_replace : forall (l : list MPropF) [A B C : MPropF], In C l -> C <> A ->
              In C (replace A B l).
  Proof.
  intros. apply in_map_iff. exists C. destruct (eq_dec_form A C) ; subst; tauto.
  Qed.

  Lemma in_replace : forall l A B C, A <> B -> In C (replace A B l) -> (In C l \/ C = B) /\ A <> C.
  Proof.
  induction l ; simpl ; intros ; auto. destruct (eq_dec_form A a) ; subst.
  inversion H0 ; subst. split ; auto. pose (IHl _ _ _ H H1). destruct a0. split ; auto. destruct H2 ; auto.
  inversion H0 ; subst. split ; auto. pose (IHl _ _ _ H H1). destruct a0 ; split ; auto. destruct H2 ; auto.
  Qed.

  Lemma notin_replace : forall l (A B : MPropF), (In A l -> False) -> replace A B l = l.
  Proof.
  induction l ; simpl ; intros ; auto. destruct (eq_dec_form A a) ; subst. destruct H ; auto. rewrite IHl ; auto.
  Qed.

  Lemma univ_gen_ext_count_occ : forall l0 l1 A, univ_gen_ext (fun x : MPropF => x = A) l0 l1 ->
                count_occ eq_dec_form l0 A <= count_occ eq_dec_form l1 A.
  Proof.
  intros. induction X ; simpl ; try lia. all: destruct (eq_dec_form x A) ; try lia.
  Qed.

  Lemma univ_gen_ext_S_count_occ : forall l0 l1 A, (count_occ eq_dec_form l0 A < count_occ eq_dec_form l1 A) ->
                (univ_gen_ext (fun x : MPropF => x = A) l0 l1) ->
                existsT2 l2 l3 l4 l5, (l0 = l2 ++ l3) * (l1 = l4 ++ A :: l5) * (univ_gen_ext (fun x : MPropF => x = A) l2 l4) *
                                              (univ_gen_ext (fun x : MPropF => x = A) l3 l5).
  Proof.
  intros. revert H. induction X ; simpl ; intros ; auto ; try lia.
  - destruct (eq_dec_form x A) ; subst.
    * assert ((count_occ eq_dec_form l A) < (count_occ eq_dec_form le A)). lia. apply IHX in H0.
      destruct H0. destruct s. destruct s. destruct s. destruct p. destruct p. destruct p ; subst.
      exists (A :: x). exists x0. exists (A :: x1). exists x2. simpl. repeat split ; auto.
      apply univ_gen_ext_cons ; auto.
    * apply IHX in H. destruct H. destruct s. destruct s. destruct s. destruct p. destruct p. destruct p ; subst.
      exists (x :: x0). exists x1. exists (x :: x2). exists x3. simpl. repeat split ; auto.
      apply univ_gen_ext_cons ; auto.
  - destruct (eq_dec_form x A) ; subst.
    * exists []. exists l. exists []. exists le. simpl. repeat split ; auto. apply univ_gen_ext_nil.
    * apply IHX in H. destruct H. destruct s. destruct s. destruct s. destruct p. destruct p. destruct p ; subst.
      exists x. exists x0. exists (A :: x1). exists x2. simpl. repeat split ; auto.
      apply univ_gen_ext_extra ; auto.
  Qed.

  Lemma count_occ_n_imp_subformLF : forall l A B, count_occ eq_dec_form l (A --> B) * S (n_imp_subformF A) <= n_imp_subformLF l.
  Proof.
  induction l ; simpl ; intros ; auto. destruct (eq_dec_form a (A --> B)) ; subst ; auto.
  - pose (IHl A B). assert (n_imp_subformF A < n_imp_subformF (A --> B)). simpl. lia. lia.
  - pose (IHl A B). lia.
  Qed.

  Lemma count_le_n_imp : forall l A B, count_occ eq_dec_form l (A --> B) <= n_imp_subformLF l.
  Proof.
  intros. pose (count_occ_n_imp_subformLF l A B). lia.
  Qed.

  Global Opaque eq_dec_form.

  Lemma n_imp_subformLF_replace : forall l A B, n_imp_subformLF (replace (A --> B) B l) =
        (n_imp_subformLF l - ((count_occ eq_dec_form l (A --> B)) * S (n_imp_subformF A))).
  Proof.
  intros. remember (count_occ eq_dec_form l (A --> B)) as n. revert Heqn. generalize dependent B.
  generalize dependent A. generalize dependent l. induction n ; simpl ; intros.
  - rewrite notin_replace ; try lia. symmetry in Heqn. rewrite <- count_occ_not_In in Heqn ; auto.
  - assert (count_occ eq_dec_form l (A --> B) > 0). lia. apply count_occ_In in H. apply in_split in H. destruct H.
    destruct H ; subst. repeat rewrite n_imp_subformLF_dist_app. simpl. repeat rewrite count_occ_app in Heqn.
    repeat rewrite replace_app. simpl. simpl in Heqn. destruct (eq_dec_form (A --> B) (A --> B)). 2: exfalso ; auto.
    assert (n = count_occ eq_dec_form (x ++ x0) (A --> B)). repeat rewrite count_occ_app ; lia.
    apply IHn in H. repeat rewrite replace_app in H. simpl in H. repeat rewrite n_imp_subformLF_dist_app.
    simpl. repeat rewrite n_imp_subformLF_dist_app in H. simpl in H.
    assert ((n_imp_subformLF (replace (A --> B) B x) + (n_imp_subformF B + n_imp_subformLF (replace (A --> B) B x0)))%nat =
    (n_imp_subformF B + ((n_imp_subformLF (replace (A --> B) B x) + n_imp_subformLF (replace (A --> B) B x0))))%nat). lia.
    rewrite H0. clear H0. rewrite H. simpl.
    assert ((n_imp_subformLF x + S (n_imp_subformF A + n_imp_subformF B + n_imp_subformLF x0) - S (n_imp_subformF A + n * S (n_imp_subformF A)))%nat =
    ((n_imp_subformLF x + n_imp_subformF A + n_imp_subformF B + n_imp_subformLF x0) - (n_imp_subformF A + n * S (n_imp_subformF A)))%nat).
    lia. rewrite H0. clear H0. rewrite Nat.sub_add_distr.
    assert ((n * S (n_imp_subformF A))%nat <= (n_imp_subformLF x + n_imp_subformLF x0)%nat).
    assert (n = (count_occ eq_dec_form x (A --> B) + (count_occ eq_dec_form x0 (A --> B)))%nat). lia.
    rewrite H0. pose (count_occ_n_imp_subformLF (x ++ x0) A B). rewrite n_imp_subformLF_dist_app in l.
    rewrite count_occ_app in l. lia. lia.
  Qed.

  Lemma univ_gen_ext_more_occ : forall l0 l1 A, univ_gen_ext (fun x : MPropF => x = A) l0 l1 ->
          (count_occ eq_dec_form l0 A) <= (count_occ eq_dec_form l1 A).
  Proof.
  intros. induction X ; simpl ; auto ; subst.
  - destruct (eq_dec_form x A) ; subst ; lia.
  - destruct (eq_dec_form A A) ; auto.
  Qed.

  Lemma univ_gen_ext_n_imp_subform : forall l0 l1 A, univ_gen_ext (fun x : MPropF => x = A) l0 l1->
                 (n_imp_subformLF l1 = (n_imp_subformLF l0 + (n_imp_subformF A * ((count_occ eq_dec_form l1 A) - (count_occ eq_dec_form l0 A))))%nat).
  Proof.
  intros. induction X ; simpl ; auto ; subst.
  - destruct (eq_dec_form x A) ; subst ; lia.
  - destruct (eq_dec_form A A). 2: exfalso ; auto. rewrite IHX. apply univ_gen_ext_more_occ in X.
    assert ((S (count_occ eq_dec_form le A) - count_occ eq_dec_form l A)%nat = S ((count_occ eq_dec_form le A) - count_occ eq_dec_form l A)).
    lia. rewrite H. lia.
  Qed.

  Lemma n_imp_subformS_ImpR_mult : forall x Γ0 Γ1 Δ0 Δ1 A B,
                              (univ_gen_ext (fun x : MPropF => x = A) (Γ0 ++ A :: Γ1) x) ->
          ((count_occ eq_dec_form (Δ0 ++ B :: Δ1) (A --> B) + count_occ eq_dec_form (Γ0 ++ A :: Γ1) A)%nat = count_occ eq_dec_form x A) ->
          (n_imp_subformS (x, replace (A --> B) B (Δ0 ++ B :: Δ1)) < n_imp_subformS (Γ0 ++ Γ1, Δ0 ++ A --> B :: Δ1)).
  Proof.
  intros. remember (count_occ eq_dec_form (Δ0 ++ B :: Δ1) (A --> B)) as n. revert H. revert Heqn. revert X.
  generalize dependent B. generalize dependent A. generalize dependent Δ1. generalize dependent Δ0.
  generalize dependent Γ1. generalize dependent Γ0. generalize dependent x. induction n.
  - simpl. intros. symmetry in Heqn. rewrite <- count_occ_not_In in Heqn. rewrite notin_replace ; auto.
    repeat rewrite count_occ_app in H. simpl in H. destruct (eq_dec_form A A). 2: exfalso ; auto.
    unfold n_imp_subformS ; simpl. repeat rewrite n_imp_subformLF_dist_app ; simpl.
    assert (n_imp_subformLF x = (n_imp_subformLF Γ0 + n_imp_subformLF Γ1 + n_imp_subformF A)%nat).
    pose (univ_gen_ext_n_imp_subform _ _ _ X). rewrite e0. repeat rewrite count_occ_app. simpl.
    destruct (eq_dec_form A A). 2: exfalso ; auto. repeat rewrite n_imp_subformLF_dist_app ; simpl. lia.
    lia.
  - intros. repeat rewrite replace_app. simpl. repeat rewrite count_occ_app in H. simpl in H. destruct (eq_dec_form A A). 2: exfalso ; auto.
    destruct (eq_dec_form (A --> B) B). exfalso. assert (length_form (A --> B) = length_form B).
    rewrite e0 ; auto. simpl in H0 ; lia. repeat rewrite count_occ_app in Heqn. simpl in Heqn.
    destruct (eq_dec_form B (A --> B)). exfalso. assert (length_form (A --> B) = length_form B).
    rewrite <- e0 ; auto. simpl in H0 ; lia.
    unfold n_imp_subformS ; simpl. repeat rewrite n_imp_subformLF_dist_app ; simpl.
    assert (n_imp_subformLF x = (n_imp_subformLF Γ0 + n_imp_subformLF Γ1 + (n_imp_subformF A * S (S n)))%nat).
    pose (univ_gen_ext_n_imp_subform _ _ _ X). rewrite e0. repeat rewrite count_occ_app. simpl.
    destruct (eq_dec_form A A). 2: exfalso ; auto. repeat rewrite n_imp_subformLF_dist_app ; simpl. lia.
    rewrite H0.
    pose (n_imp_subformLF_replace Δ0 A B). rewrite e0. clear e0.
    pose (n_imp_subformLF_replace Δ1 A B). rewrite e0. clear e0.
    pose (count_occ_n_imp_subformLF Δ0 A B).
    pose (count_occ_n_imp_subformLF Δ1 A B). lia.
Qed.

  (* This lemma is crucial. *)

  Lemma mult_ImpR : forall s0 s1 A B,
          InT s1 (Canopy (repeat A (count_occ eq_dec_form (snd s0) (A --> B)) ++ (fst s0), replace (A --> B) B (snd s0))) ->
          InT s1 (Canopy s0).
  Proof.
  intros s0 s1 A B. remember (count_occ eq_dec_form (snd s0) (A --> B)) as n. revert Heqn. generalize dependent B.
  generalize dependent A. generalize dependent s1. generalize dependent s0. induction n ; simpl ; intros.
  - symmetry in Heqn. rewrite <- count_occ_not_In in Heqn. rewrite notin_replace in H ; auto. destruct s0 ; auto.
  - assert (count_occ eq_dec_form (snd s0) (A --> B) > 0). lia. apply count_occ_In in H0. destruct s0. simpl in H0.
    simpl in H. simpl in Heqn. apply In_InT in H0. apply InT_split in H0. destruct H0. destruct s ; subst.
    repeat rewrite count_occ_app in Heqn. simpl in Heqn. repeat rewrite replace_app in H.
    simpl in H. destruct (eq_dec_form (A --> B) (A --> B)). 2: exfalso ; auto.
    assert (n = count_occ eq_dec_form (x ++ x0) (A --> B)). repeat rewrite count_occ_app ; lia.
    pose (IHn (A :: l, x ++ B :: x0) s1 A B). simpl in i.
    repeat rewrite count_occ_app in i. simpl in i. repeat rewrite replace_app in i.
    simpl in i. destruct (eq_dec_form B (A --> B)). exfalso. assert (length_form (A --> B) = length_form B). rewrite <- e0 ; auto.
    simpl in H1 ; lia. destruct (eq_dec_form (A --> B) B). exfalso. assert (length_form (A --> B) = length_form B). rewrite e0 ; auto.
    simpl in H1 ; lia. repeat rewrite count_occ_app in H0. pose (i H0).
    assert (repeat A n ++ A :: l = A :: repeat A n ++ l). assert (repeat A n ++ A :: l = (repeat A n ++ [A]) ++ l).
    rewrite <- app_assoc. auto. rewrite H1. rewrite <- repeat_cons. simpl ; auto. rewrite H1 in i0. apply i0 in H.
    apply ImpRule_Canopy with (prems:=[(A :: l, x ++ B :: x0)]) (prem:=(A :: l, x ++ B :: x0)) ; auto.
     left. epose (ImpRRule_I _ _ []). simpl in i1 ; apply i1. apply InT_eq.
  Qed.