(**************************************************************)
(*   Copyright Ian Shillito *                 *)
(*                                                            *)
(*                             * Affiliation ANU  *)
(**************************************************************)
(*      This file is distributed under the terms of the       *)
(*         CeCILL v2.1 FREE SOFTWARE LICENSE AGREEMENT        *)
(**************************************************************)

Certification of uniform interpolant function (define below)

  Require Import List Extraction.
  Require Import Lia.
  Require Import String.

  Require Import KS_export.

  Require Import UIK_Def_measure.
  Require Import UIK_Canopy.
  Require Export UIK_basics.

  Import ListNotations.

  #[local] Infix "∈" := (@In _) (at level 70, no associativity).

  Section imap.

  Variables (X Y : Type)
            (F : X Y Prop) (* We will instantiate F with GUI (to have mutal recursion). *)
            (Ffun : x l m, F x l F x m l = m) (*Require that F is a function. *)
            (D : X Prop) (* Domain of F *)
            (f : x, D x sig (F x)). (* F is defined on the domain. *)

  (* Proceed similarly as Dominique did with flatmap. *)

  Inductive Gimap : list X list Y Prop :=
    | Gim_nil : Gimap [] []
    | Gim_cons {x y l m} : F x y
                          Gimap l m
                          Gimap (x::l) (y::m).

  Hint Constructors Gimap : core.

  Fact Gimap_inv_left l m :
        Gimap l m
       match l with
        | [] [] = m
        | x::l y m', F x y Gimap l m' m = y :: m'
        end.
  Proof. destruct 1; eauto. Qed.

  Fact Gimap_inv_sg_left x m : Gimap [x] [m] F x m.
  Proof.
    intros. apply Gimap_inv_left in H. destruct H. destruct H. destruct H.
    destruct . inversion . subst. auto.
  Qed.


  Fact Gimap_app_inv_left l1 l2 m :
        Gimap () m
       m1 m2, Gimap Gimap m = .
  Proof.
    induction as [ | x ] in m |- *; simpl.
    + exists [], m; auto.
    + intros (y & m' & & ( & & & & )% & )%Gimap_inv_left.
      exists (y::), . repeat split ; auto.
  Qed.


  Fixpoint imap l : ( x, x l D x) sig (Gimap l).
  Proof.
    refine (match l with
    | [] fun _ exist _ [] Gim_nil
    | x::l fun dl let (y,hy) := f x _ in
                    let (m,hm) := imap l _ in
                    exist _ (y::m) (Gim_cons hy hm)
    end); auto.
    apply dl ; apply in_eq. intros. apply dl ; apply in_cons ; auto.
  Defined.


  Variables (g : X Y) (Hg : x, F x (g x)).

  Fact Gimap_map l : Gimap l (map g l).
  Proof. induction l; simpl; now constructor. Qed.

  Fact Gimap_fun l0 : l1 l2, Gimap Gimap = .
  Proof. induction . intros. apply Gimap_inv_left in H. subst.
             apply Gimap_inv_left in . auto. intros.
             apply Gimap_inv_left in H. apply Gimap_inv_left in .
             destruct H. destruct H. destruct H. destruct . destruct .
             destruct . destruct . destruct . subst. pose (Ffun _ _ _ H ).
             rewrite e. pose ( _ _ ). rewrite . auto. Qed.


  End imap.

Arguments Gimap {X} {Y} _.
Arguments imap {X} {Y} _ {D} _ {l}.

  Section Gimap_cont.

  Variables (X Y : Type)
            (F : X Y Prop) (* We will instantiate F with GUI (to have mutal recursion). *)
            (D : X Prop) (* Domain of F *)
            (f : x, D x sig (F x)). (* F is defined on the domain. *)

  Fact Gimap_fun_rest l0 : l1 l2, ( x, InT x y0 y1, F x F x = ) Gimap F Gimap F = .
  Proof. induction . intros Dom H . apply Gimap_inv_left in H. subst.
             apply Gimap_inv_left in . auto. intros Dom H .
             apply Gimap_inv_left in H. apply Gimap_inv_left in .
             destruct H. destruct H. destruct H. destruct . destruct .
             destruct . destruct . destruct . subst.
             assert (: InT a (a :: )). apply InT_eq.
             pose (Dom _ _ _ H ). rewrite e.
             assert (: x : X, InT x y0 y1 : Y, F x F x = ).
             intros. apply Dom with (x:=) ; auto. apply InT_cons ; auto.
             pose ( _ _ ). rewrite . auto. Qed.


  End Gimap_cont.

  Section UI.

  (* I define the graph of the function UI. *)

  Variables (p : string). (* The variable we exclude from the interpolant. *)

  Unset Elimination Schemes.

  Inductive GUI : Seq MPropF Prop :=
    | GUI_empty_seq {s} : (s = ([],[])) (* If the sequent is empty, output Bot. *)
                                      GUI s Bot
    | GUI_critic_init {s} : critical_Seq s (* If critical and initial, output Top. *)
                                      is_init s
                                      GUI s Top
    | GUI_not_critic {s l} : ((critical_Seq s) False) (* If not critical, output conjunction of recursive calls of GUI on Canopy. *)
                                      (Gimap GUI (Canopy s) l)
                                      GUI s (list_conj l)
    | GUI_critic_not_init {s l A} : critical_Seq s (* If critical but not initial, store the propositional variables, recursively call on
                                                                                                               the KR premises of the sequent, and consider the diamond jump. *)

                                           (s ([],[]))
                                           (is_init s False)
                                           (Gimap GUI (KR_prems s) l)
                                           (GUI (unboxed_list (top_boxes (fst s)), []) A)
                                           GUI s (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 l))
                                                     (Diam A)))).

 Set Elimination Schemes.

  Lemma GUI_fun : x l m, GUI x l GUI x m l = m.
  Proof.
  apply (LtSeq_ind (fun x l m, GUI x l GUI x m l = m)).
  intros s IH l m H . subst. inversion H ; inversion ; subst ; auto ; simpl in *. 1-8: exfalso ; auto.
  6-9: exfalso ; auto. 1,3: apply not_init_empty_set ; auto. apply ; apply critical_empty_set.
  apply ; apply critical_empty_set.
  - assert (: ( x : Seq, InT x (Canopy s) y0 y1 : MPropF, GUI x GUI x = )).
    intros. apply IH with (:=x) ; auto. destruct (Canopy_LtSeq s x ) ; subst ; auto.
    exfalso. apply . apply Canopy_critical with (s:=x) ; subst ; auto.
    pose (Gimap_fun_rest _ _ GUI (Canopy s) ). rewrite e ; auto.
  - assert (: list_disj (map Box ) = list_disj (map Box )).
    assert (: ( x : Seq, InT x (KR_prems s) y0 y1 : MPropF, GUI x GUI x = )).
    intros. apply IH with (:=x) ; auto. apply KR_prems_LtSeq ; auto.
    pose (Gimap_fun_rest _ _ GUI (KR_prems s) ). rewrite e ; auto.
    assert (: A = ).
    { destruct (eq_dec_listsF (fst s) []) ; subst.
       - rewrite e in * ; simpl in *. inversion ; inversion ; subst ; auto.
         1-14: exfalso ; auto. 1,3: apply not_init_empty_set ; auto.
         1-3: pose critical_empty_set ; auto.
       - apply IH with (:=(unboxed_list (top_boxes (fst s)), [])) ; auto.
         unfold LtSeq. unfold measure ; simpl.
         pose (size_LF_nil_unbox_top_box _ n). lia. }
    subst. rewrite . auto.
  Qed.


  Definition GUI_tot : s : Seq, {A : MPropF | GUI s A}.
  Proof.
  apply (LtSeq_ind (fun x existsT A : MPropF, GUI x A)).
  intros s IH. destruct (empty_seq_dec s).
  - subst. exists Bot. apply GUI_empty_seq ; auto.
  - destruct (critical_Seq_dec s).
    -- destruct (dec_KS_init_rules s).
      * assert (is_init s) ; auto. exists Top. apply GUI_critic_init ; auto.
      * assert (is_init s False) ; auto.
        assert (( x : Seq, In x (KR_prems s) {x0 : MPropF | GUI x })).
        intros. apply IH with (:=x) ; auto. apply KR_prems_LtSeq ; auto. apply InT_In_Seq ; auto.
        epose (@imap _ _ GUI (fun (x : Seq) In x (KR_prems s)) (KR_prems s)). simpl in . destruct ; auto.
        destruct (eq_dec_listsF (fst s) []).
        + exists (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 x)) (Diam Bot)))). apply GUI_critic_not_init ; auto. rewrite e ; simpl.
           apply GUI_empty_seq ; auto.
        + assert (: existsT A : MPropF, GUI (unboxed_list (top_boxes (fst s)), []%list) A). apply IH.
           unfold LtSeq. unfold measure. simpl. pose (size_LF_nil_unbox_top_box (fst s) ). lia. destruct .
           exists (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 x)) (Diam )))). apply GUI_critic_not_init ; auto.
    -- assert (( x : Seq, In x (Canopy s) {x0 : MPropF | GUI x })).
      intros. apply IH with (:=x) ; auto. destruct (Canopy_LtSeq s x) ; auto.
      apply InT_In_Seq ; auto. subst. exfalso. apply f. apply InT_In_Seq in H ; apply Canopy_critical in H ; auto.
      epose (@imap _ _ GUI (fun (x : Seq) In x (Canopy s)) H (Canopy s)). simpl in . destruct ; auto.
      exists (list_conj x). apply GUI_not_critic ; auto.
  Defined.


  Fact GUI_inv_empty_seq {s A} : GUI s A s = ([],[]) Bot = A.
  Proof. intros. pose (GUI_empty_seq ). apply (GUI_fun _ _ _ g H). Qed.

  Fact GUI_inv_critic_init {s A} : GUI s A critical_Seq s is_init s Top = A.
  Proof. intros. pose (GUI_critic_init X). apply (GUI_fun _ _ _ g H). Qed.

  Fact GUI_inv_not_critic {s A l} : GUI s A (critical_Seq s False)
                           (Gimap GUI (Canopy s) l)
                           ((list_conj l) = A).
  Proof.
  intros. pose (GUI_not_critic ). apply (GUI_fun _ _ _ g H).
  Qed.


  Fact GUI_inv_critic_not_init {s A B l0 } : GUI s A critical_Seq s
                           (s ([],[]))
                           (is_init s False)
                           (Gimap GUI (KR_prems s) )
                           (GUI (unboxed_list (top_boxes (fst s)), []) B)
                           ((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 ))
                                                     (Diam B)))) = A).
  Proof.
  intros. pose (GUI_critic_not_init ). apply (GUI_fun _ _ _ g H).
  Qed.


  Let UI_pwc : x, sig (GUI x).
  Proof.
  apply GUI_tot.
  Qed.


  Definition UI x := proj1_sig (UI_pwc x).

  Fact UI_spec x : GUI x (UI x).
  Proof. apply (proj2_sig _). Qed.

  Lemma UI_GUI : x A, UI x = A GUI x A.
  Proof.
  intros. split ; intro ; subst.
  apply UI_spec. unfold UI. destruct UI_pwc. simpl.
  apply GUI_fun with (x:=x) ; auto.
  Qed.


  End UI.