(****************************************************************
  Copyright © 2009-2024  Amy Felty

  This file is part of "Two-Level Hybrid in Coq"

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <https://www.gnu.org/licenses/>.
  ***************************************************************)
(****************************************************************
   File: sl.v                                                 

   - original: May 2009
   - Apr 2024: Current Coq Version: V8.18.0
                                                                 
   An intuitionistic sequent calculus used as a specification
   logic, for use in two-level Hybrid.  See:

   Amy Felty and Alberto Momigliano, "Hybrid: A Definitional Two Level
   Approach to Reasoning with Higher-Order Abstract Syntax."  Journal
   of Automated Reasoning, 48(1):43-105, 2012.
  ***************************************************************)

Require Export Compare.
From HybridSys Require Export Hybrid.

Section sl.

Set Implicit Arguments.

(****************************************************************
   The intuitionistic specification logic.
  ****************************************************************)
Variable atm : Set.
Variable con : Set.

Inductive oo : Set :=
  | atom : atm -> oo
  | T : oo
  | Conj : oo -> oo -> oo
  | Imp : atm -> oo -> oo
  | All : (expr con -> oo) -> oo
  | Some : (expr con -> oo) -> oo.

Variable prog : atm -> oo -> Prop.

Inductive seq : nat -> list atm -> oo -> Prop :=
  | s_bc :
      forall (i : nat) (A : atm) (L : list atm) (b : oo),
      prog A b -> seq i L b -> seq (i+1) L (atom A)
  | s_init :
      forall (i : nat) (A A' : atm) (L : list atm),
      In A (A' :: L) -> seq i (A' :: L) (atom A)
  | s_tt : forall (i : nat) (L : list atm), seq i L T
  | s_and :
      forall (i : nat) (B C : oo) (L : list atm),
      seq i L B -> seq i L C -> seq (i+1) L (Conj B C)
  | s_imp :
      forall (i : nat) (A : atm) (B : oo) (L : list atm),
      seq i (A :: L) B -> seq (i+1) L (Imp A B)
  | s_all :
      forall (i : nat) (B : expr con -> oo) (L : list atm),
      (forall x : expr con, proper x -> seq i L (B x)) -> seq (i+1) L (All B)
  | s_some :
      forall (i : nat) (B : expr con -> oo) (L : list atm) (x : expr con),
      seq i L (B x) -> seq (i+1) L (Some B).

Definition seq' (L : list atm) (B : oo) : Prop := exists i : nat, seq i L B.

(* height weakening *)
Theorem seq_mono :
 forall (j k : nat) (l : list atm) (b : oo), j < k -> seq j l b -> seq k l b.
intro j.
generalize
 (lt_wf_ind j
    (fun j : nat =>
     forall (k : nat) (l : list atm) (b : oo),
     j < k -> seq j l b -> seq k l b)).
intros H k l b H0 H1.
apply H; auto.
clear H1 H0 b l k H j.
intros n H k l b; generalize b l k; clear b l k.
simple induction b.
- intros a l k H0 H1.
  inversion H1.
  + subst.
    specialize lt_non_zero with (1 := H0); intro H7.
    elim H7; clear H7; intros j' H7.
    subst.
    apply s_bc with b0; auto.
    apply H with i; try lia; auto.
  + subst.
    apply s_init; auto.
- intros l k H0 H1.
  apply s_tt; auto.
- intros p H0 p0 H1 l k H2 H3.
  clear H0 H1.
  inversion H3.
  subst.
  specialize lt_non_zero with (1 := H2); intro H0.
  elim H0; clear H0; intros j' H0.
  subst.
  apply s_and; auto.
  + apply H with i; try lia; auto.
  + apply H with i; try lia; auto.
- intros a p H0 l k H1 H2.
  clear H0.
  inversion H2.
  subst.
  specialize lt_non_zero with (1 := H1); intro H0.
  elim H0; clear H0; intros j' H0.
  subst.
  apply s_imp; auto.
  apply H with i; try lia; auto.
- intros p H0 l k H1 H2.
  clear H0.
  inversion H2.
  subst.
  specialize lt_non_zero with (1 := H1); intro H0.
  elim H0; clear H0; intros j' H0.
  subst.
  apply s_all; auto.
  intros x HP.
  apply H with i; try lia; auto.
- intros p H0 l k H1 H2.
  clear H0.
  inversion H2.
  subst.
  specialize lt_non_zero with (1 := H1); intro H0.
  elim H0; clear H0; intros j' H0.
  subst.
  apply s_some with x; auto.
  apply H with i; try lia; auto.
Qed.

Theorem seq_mono_cor :
 forall (j k : nat) (l : list atm) (b : oo),
 j <= k -> seq j l b -> seq k l b.
 intros j k l b H H0.
specialize le_decide with (1:=H); intro H1.
elim H1; clear H1; intro H1.
- apply seq_mono with j; auto.
- rewrite <- H1; auto.
Qed.

(* context weakening *)
Theorem seq_thin_exch :
 forall (j : nat) (b : oo) (l l' : list atm),
 (forall a : atm, In a l -> In a l') -> seq j l b -> seq j l' b.
intros j b l l' H H0.
generalize H0 l' H; clear H0 H l'.
simple induction 1.
- intros i A L b0 H H1 H2 l' H3.
  apply s_bc with b0; auto.
- intros i A A' L H l' H1.
  generalize (H1 A H).
  clear H1; generalize l'; clear l'.
  induction l'.
  + intro H1; elim H1; auto.
  + apply s_init; auto.
- intros; apply s_tt.
- intros i B C L H H1 H2 H3 l' H4.
  apply s_and; auto.
- intros i A B L H H1 l' H2.
  apply s_imp; auto.
  apply H1; auto.
  intros a H3.
  specialize in_inv with (1 := H3); intro H4.
  elim H4; clear H4; intro H4.
  + rewrite H4.
    apply in_eq; auto.
  + apply in_cons; apply H2; auto.
- intros i B L H H1 l' H2.
  apply s_all; auto.
- intros i B L x H H1 l' H2.
  apply s_some with x; auto.
Qed.

Theorem specialization :
  forall (i:nat) (l:list atm) (b:expr con -> oo),
  seq (i+1) l (All b) -> forall (x:expr con), proper x -> seq i l (b x).
Proof.
intros i l b h0 x h1.
inversion h0; subst.
assert (i=i0); try lia; subst; clear H.
apply H2; auto.
Qed.

(* atomic cut *)
Lemma seq_cut_aux:
  forall (i j:nat)(a:atm)(b:oo)(l:(list atm)),
  (seq i (a::l) b)->(seq j l (atom a))->(seq (i+j) l b).
Proof.
intro i.
generalize (lt_wf_ind
              i
              (fun i:nat =>
              forall (j:nat)(a:atm)(b:oo)(l:(list atm)),
              (seq i (cons a l) b)->(seq j l (atom a))->(seq (plus i j) l b))).
intros H j a b l H0 H1.
apply H with a; auto.
clear H1 H0 l b a j H i.
intros n H j a b.
generalize b j a; clear b j a.
induction b.
- intros j a0 l H0 H1.
  inversion H0; subst.
  + replace (i+1+j) with (i+j+1); try lia.
    apply s_bc with b; auto.
    apply H with a0; auto; lia.
  + simpl in H4.
    elim H4; clear H4; intro H4.
    * subst.
      apply seq_mono_cor with j; auto; try lia.
    * clear H0 H1; generalize l H4; clear l H4.
      induction l; auto.
      ++ simpl; intro H0; elim H0.
      ++ intro H0; apply s_init; auto.
(* T case *)
- intros; apply s_tt; auto.
(* Conj case *)
- intros j a l H2 H3.
  inversion H2; subst.
  replace (i+1+j) with (i+j+1); try lia.
  apply s_and; auto.
  + apply H with a; auto; lia.
  + apply H with a; auto; lia.
(* Imp case *)
- intros j a0 l H1 H2.
  inversion H1; subst.
  replace (i+1+j) with (i+j+1); try lia.
  apply s_imp; auto.
  apply H with a0; auto; try lia.
  + apply seq_thin_exch with (a::a0::l); auto.
    intros a1 H0.
    simpl; simpl in H0.
    tauto.
  + apply seq_thin_exch with l; auto.
    simpl; intros a1 H0; tauto.
(* All case *)
- intros j a l H1 H2.
  inversion H1; subst.
  replace (i+1+j) with (i+j+1); try lia.
  apply s_all; auto.
  intros x H'.
  apply H with a; auto; try lia.
(* Some case *)
- intros j a l H1 H2.
  inversion H1; subst.
  replace (i+1+j) with (i+j+1); try lia.
  apply s_some with x; auto.
  apply H with a; auto; try lia.
Qed.

Theorem seq_cut:
  forall (a:atm)(b:oo)(l:(list atm)),
  (seq' (a::l) b)->(seq' l (atom a))->(seq' l b).
unfold seq'.
intros a b l [i H] [j H0].
exists (i + j).
apply seq_cut_aux with a; auto.
Qed.

Unset Implicit Arguments.

End sl.
