Require Import GQM.Base.
Require Import GQM.GQM.

(** Definition *)

Inductive GQMded: form -> Prop :=
| gqm_mp A B: GQMded (A ->> B) -> GQMded A -> GQMded B
| gqm_I1 A B: GQMded (A ->> B ->> A)
| gqm_I2 A B C: GQMded ((A ->> B ->> C) ->> (A ->> B) ->> A ->> C)
| gqm_I3 A B: GQMded ((-! A ->> -! B) ->> B ->> A)
| gqm_swap A B C: GQMded ((A ->> B ->> C) ->> (B ->> A ->> C)) (* TODO prove that this follows from the rest *)
(* | gqm_dneg f: GQMded (-! (-! f) ->> f) *)

| gqm_ADist A B: GQMded ([A](A ->> B) ->> ([A]A ->> [A]B))
| gqm_AE A B: GQMded ([A]A ->> (A.[B.: ids])).

Notation "'|--' A"  := (GQMded A) (at level 80, no associativity).
Hint Constructors GQMded : GQMDB.

(** Lemmas for classical tautologies *)

Lemma gqm_negE : forall f, |-- (Bot ->> f).
  move => f.
  by apply: gqm_AE.
Qed.

Lemma gqm_id : forall A, |-- (A ->> A).
  move => A.
  have H1 : (|-- A ->> (A ->> A) ->> A) by apply: gqm_I1.
  have H2 : (|-- (A ->> (A ->> A) ->> A) ->> (A ->> A ->> A) ->> A ->> A) by apply: gqm_I2.
  have H3 : (|-- (A ->> A ->> A) ->> A ->> A) by apply: gqm_mp; eauto.
  have H4 : (|-- A ->> A ->> A) by apply: gqm_I1.
  apply: gqm_mp.
  - by apply: H3.
  - by eauto.
Qed.

Lemma gqm_id' : forall A, |-- (A ->> A).
  eauto with GQMDB.
  Unshelve.
  apply: (Var 0).
  apply: (Var 0).
Qed.

Lemma gqm_id'' : forall A, |-- (A ->> A).
  move => A.
  apply: (gqm_mp (A ->> A ->> A)); last by apply: gqm_I1.
  apply: (gqm_mp (A ->> (A ->> A) ->> A)); last by apply: gqm_I1.
  by apply: gqm_I2.
Qed.

Lemma gqm_appl : forall A B, |-- ((A ->> B) ->> A ->> B).
Proof.
  eauto with GQMDB.
Qed.

Lemma gqm_appl2 : forall A B, |-- (A ->> (A ->> B) ->> B).
Proof.
  eauto with GQMDB.
  Unshelve.
  apply: (Var 0).
  apply: (Var 0).
Qed.

Lemma gqm_chain : forall A B C, |-- (B ->> C) ->> (A ->> B) ->> (A ->> C).
Proof.
  move => A B C.
  apply: gqm_mp.
  - apply: gqm_mp.
    + apply: (gqm_I2 (B ->> C) (A ->> B ->> C) ((A ->> B) ->> A ->> C)).
    + apply: gqm_mp.
      * apply: (gqm_I1 ((A ->> B ->> C) ->> (A ->> B) ->> A ->> C) (B ->> C)).
      * apply: (gqm_I2 A B C).
  - apply: (gqm_I1 (B ->> C) A).
Qed.

Lemma gqm_discard : forall A B C, |-- (A ->> C) ->> (B ->> A ->> C).
Proof.
  eauto with GQMDB.
Qed.

Lemma gqm_discardr : forall A B C, |-- (A ->> C) ->> (A ->> B ->> C).
Proof.
  eauto with GQMDB.
Qed.

Lemma gqm_discardI : forall A B, |-- (A ->> A ->> B) ->> (A ->> B).
Proof.
  move => A B.
  apply: gqm_mp.
  - apply: gqm_mp.
    + apply: gqm_I2.
      apply: (A ->> A).
    + apply: gqm_I2.
  - apply: gqm_mp.
    + apply: gqm_discard.
    + apply: gqm_id.
Qed.

Lemma gqm_discardIr : forall A B C, |-- (A ->> B ->> A ->> C) ->> (A ->> B ->> C).
Proof.
  eauto with GQMDB.
Qed.

Lemma gqm_dnegI : forall A, |-- (A ->> -! -! A).
Proof.
  move => A.
  unfold Neg.
  apply: gqm_mp.
  - apply: gqm_swap.
  - apply: gqm_appl.
Qed.

Lemma gqm_dnegE : forall A, |-- (-! -! A ->> A).
Proof.
  move => A.
  apply: gqm_mp.
  - apply: gqm_I3.
  - apply: gqm_dnegI.
Qed.

Lemma gqm_em : forall A, |-- (-! A ||| A).
Proof.
  apply: gqm_dnegE.
Qed.

(* Lemma gqm_em : forall f, |-- (-! f ||| f). *)
(* Proof. *)
  (* unfold Or. *)
(* Qed. *)

Lemma gqm_andI : forall A B, |-- (A ->> B ->> A & B).
Proof.
  move => A B.
  unfold And.
  unfold Neg.
  apply: gqm_mp.
  - apply: gqm_mp.
    + apply: gqm_chain.
      apply: ((A ->> B ->> Bot) ->> B ->> Bot).
    + apply: gqm_swap.
  - apply: gqm_mp.
    + apply: gqm_swap.
    + apply: gqm_id.
Qed.

Lemma gqm_andE1 : forall A B, |-- (A & B) ->> A.
Proof.
  move => A B.
  unfold And.
  unfold Neg.
  apply: gqm_mp.
  - apply: gqm_mp.
    + apply: gqm_chain.
      apply: (-! -! A).
    + apply: gqm_dnegE.
  - unfold Neg.
    apply: gqm_mp.
    + apply: gqm_swap.
    + apply: gqm_mp.
      * apply: gqm_mp.
        -- apply: gqm_chain.
           apply: (A ->> B ->> Bot).
        -- apply: gqm_appl2.
      * eauto with GQMDB.
Qed.

Lemma gqm_andE2 : forall A B, |-- (A & B) ->> B.
Proof.
  move => A B.
  unfold And.
  unfold Neg.
  apply: gqm_mp.
  - apply: gqm_mp.
    + apply: gqm_chain.
      apply: (-! -! B).
    + apply: gqm_dnegE.
  - unfold Neg.
    apply: gqm_mp; first by apply: gqm_swap.
    apply: gqm_mp.
    + apply: gqm_mp.
      * apply: gqm_chain.
           apply: (A ->> B ->> Bot).
      * apply: gqm_appl2.
    + apply: gqm_I1.
Qed.

Lemma gqm_orI1 : forall A B, |-- A ->> A ||| B.
Proof.
  move => A B.
  rewrite /Or /Neg.
  apply: gqm_mp; first by apply: gqm_swap.
  apply: gqm_mp; first by apply: gqm_chain.
  apply: gqm_negE.
Qed.

Lemma gqm_orI2 : forall A B, |-- B ->> A ||| B.
Proof.
  move => A B.
  rewrite /Or.
  apply: gqm_I1.
Qed.

(* Lemma gqm_orE : forall A B C, |-- (A ->> C) ->> (B ->> C) ->> (A ||| B) ->> C. *)
(* Proof. *)
  (* move => A B C. *)
  (* rewrite /Or. *)