Library libs.fset_tac

Author: Alexander Anisimov
From mathcomp Require Import ssreflect ssrbool eqtype seq choice.
From libs Require Import edone bcase base fset.

Set Implicit Arguments.

Automation for finite Sets


Section TabRules.

  Variable T: choiceType.

Simplification rules
composite set operations and relations
  Lemma properSub (A B : {fset T}): A `<` B = (A `<=` B) && ~~ (B `<=` A).
  Proof. rewrite properEneq eqEsub. by case (A`<=`B), (B`<=`A). Qed.
  Lemma sepI (A B : {fset T}) : A `&` B = [fset x in A | x \in B].
  Proof. by rewrite /fsetI. Qed.
  Lemma sepD (A B : {fset T}) : A `\` B = [fset x in A | x \notin B].
  Proof. by rewrite /fsetD. Qed.

  Definition compSetOps := (sepI, sepD, properSub).


Tableau rules

nonbranching
one premise
  Lemma prop_dn s : ~~ ~~ s s.   Proof. exact (@ negbNE s). Qed.

  Lemma prop_conj s t : s && t s t.   Proof. apply: andP. Qed.

  Lemma prop_negDisj s t : ~~ (s || t) ~~ s ~~ t.   Proof. rewrite negb_or. apply: andP. Qed.

  Lemma prop_negImpl s t : ~~ (s ==> t) s ~~ t.   Proof. rewrite negb_imply. apply: andP. Qed.


  Lemma satr_eqSym (a b : T) : a == b b == a.   Proof. by rewrite eq_sym. Qed.

  Lemma satr_memSgt (a b : T) : a \in [fset b] a == b.   Proof. by rewrite in_fset1. Qed.

  Lemma satr_nmemSgt (a b : T) : a \notin [fset b] a != b.   Proof. by rewrite in_fset1. Qed.

  Lemma satr_sgtSub (a:T) (A : {fset T}) : [fset a] `<=` A a \in A.   Proof. by rewrite fsub1. Qed.

  Lemma satr_eqP (A B : {fset T}) : A == B (A `<=` B) (B `<=` A).   Proof. rewrite eqEsub. apply: andP. Qed.

  Lemma satr_nmemU (a:T) (A B : {fset T}) : a \notin A `|` B a \notin A a \notin B.   Proof. rewrite in_fsetU negb_or. apply: andP. Qed.

  Lemma pow_mem (A B : {fset T}) : A \in (powerset B) A `<=` B.   Proof. by rewrite powersetE. Qed.

several premises
  Lemma satr_memSub (a:T) (A B : {fset T}) : a \in A A `<=` B a \in B.   Proof. moveP Q. apply: (@subP T A) ⇒ //. Qed.

  Lemma satr_nmemSup (a:T) (A B : {fset T}) : a \notin A B `<=` A a \notin B.   Proof. moveP Q. case R: (a \in B) ⇒ //. by rewrite (subP Q) in P. Qed.

  Lemma satr_eqMem (a b : T) (A : {fset T}) : a == b b \in A a \in A.   Proof. intros P Q. by rewrite -(eqP P) in Q. Qed.

  Lemma satr_eqTrans (a b c : T) : a == b b == c a == c.   Proof. intros P Q. by rewrite -(eqP P) in Q. Qed.

fresh variable generation
  Lemma satr_notSub (A B : {fset T}) : ~~ (A `<=` B) a, (a \in A) (a \notin B).   Proof. moveP. move: (subPn A B P) ⇒ {P} [a P Q]. by a. Qed.

  Lemma pow_nmem (A B : {fset T}) : A \notin (powerset B) x, (x \in A) (x \notin B).   Proof. rewrite powersetEP. apply (satr_notSub A B P). Qed.


branching

  Lemma prop_disj s t : s || t s t.   Proof. apply: orP. Qed.

  Lemma prop_impl s t : s ==> t ~~ s t.   Proof. rewrite implybE. apply: orP. Qed.

  Lemma prop_negConj s t : ~~ (s && t) ~~ s ~~ t.   Proof. rewrite negb_and. apply: orP. Qed.

  Lemma satr_memU (a:T) (A B : {fset T}) : a \in A `|` B a \in A a \in B.   Proof. rewrite in_fsetU. apply: orP. Qed.

  Lemma satr_neqP (A B : {fset T}) :
    A != B x, (x \in A x \notin B) (x \in B x \notin A).   Proof.
    rewrite eqEsub negb_andP. move: (orP P) ⇒ {P} [P|P].
    - move: (satr_notSub A B P) ⇒ {P} [x [P Q]]. x; by left.
    - move: (satr_notSub B A P) ⇒ {P} [x [P Q]]. x; by right.
  Qed.


branch closing

  Lemma clos_posNeg (s : bool) : s ~~ s False.   Proof. by case s. Qed.

  Lemma clos_neqxx (a : T) : a != a False.   Proof. by rewrite eqxx. Qed.

  Lemma clos_memEmp (a : T) : a \in fset0 False.   Proof. by rewrite in_fset0. Qed.

substitution

  Lemma subst_memSep (y:T) (A : {fset T}) (p : T bool) : y \in sep A p (y \in A) (p y).   Proof. rewrite in_sep. apply: andP. Qed.

  Lemma subst_nmemSep (y:T) (A : {fset T}) (p : T bool):
    y \notin [ fset x in A | p x] y \notin A ~~ p y.   Proof. rewrite in_sepP . apply: orP. by rewrite -negb_and. Qed.

cut

  Lemma cut_eq (a b : T) : a == b a != b.
  Proof. case (a == b); first by left. right. by apply negbT. Qed.
  Lemma cut_mem a (A: {fset T}) : a \in A a \notin A.   Proof. case (a \in A); first by left. right. by apply negbT. Qed.

  Lemma cut_sub (A B : {fset T}) : A `<=` B ~~ (A `<=` B).   Proof. case (A `<=` B); first by left. right. by apply negbT. Qed.

End TabRules.

Implementation of the Tactic

adding conclusions of diffrerent rule types

Ltac extend X PX :=
  let V := fresh "H" in
  let X := constr:(is_true X) in
  match goal with
    | _: X |- _fail 1
    | _have V: X by apply PX
  end.

Ltac extend_conj A B PX :=
  let U := fresh "H" in
  let V := fresh "H" in
  let A := (eval simpl in (is_true A)) in
  let B := (eval simpl in (is_true B)) in
  match goal with
    | _: A, _: B |- _fail 1
    | _have [U V]: AB by apply PX
  end.

Ltac extend_disj A B PX :=
  let V := fresh "H" in
  let A := (eval simpl in (is_true A)) in
  let B := (eval simpl in (is_true B)) in
  match goal with
    | _: A |- _fail 1
    | _: B |- _fail 1
    | _have [V | V]: AB by apply PX
  end.

Ltac extend_conj_ex A B PX :=
  let X := fresh "x" in
  let U := fresh "H" in
  let V := fresh "H" in
  match goal with
    | _: is_true(?x \in A), _: is_true (?x \notin B) |- _fail 1
    | _have [X [U V]]: x, (x \in A) ∧ (x \notin B) by apply PX
  end.

Ltac extend_disconj_ex A B PX :=
  let X := fresh "x" in
  let U := fresh "H" in
  let V := fresh "H" in
  match goal with
    | _: is_true(?x \in A), _: is_true(?x \notin B) |- _fail 1
    | _: is_true(?x \in B), _: is_true(?x \notin A) |- _fail 1
    | _have [X [[U V] | [U V]]]:
              x, (x \in Ax \notin B) ∨ (x \in Bx \notin A)
               by apply PX
  end.

saturation

Ltac closebranch :=
  match goal with
  | H0: is_true (?s), H1: is_true(~~ ?s) |- _apply (@ clos_posNeg s); first exact H0; exact H1
  | H0: is_true (?a != ?a) |- _apply (@ clos_neqxx _ a); exact H0
  | H0: is_true (?a \in fset0) |- _apply (@ clos_memEmp _ a); exact H0
  end.

Ltac nonbranching :=
  match goal with
  
  | _: is_true(~~ ~~ ?s) |- _extend s (prop_dn s)
  | _: is_true(?s && ?t) |- _extend_conj s t (prop_conj s t)
  | _: is_true(~~ (?s || ?t)) |- _extend_conj (~~ s) (~~ t) (prop_negDisj s t)
  | _: is_true(~~ (?s ==> ?t)) |- _extend_conj s (~~ t) (prop_negImpl s t)
  | _: is_true(?a == ?b) |- _extend (b == a) (satr_eqSym a b)
  | _: is_true(?a \in [fset ?b]) |- _extend (a == b) (satr_memSgt _ a b)
  | _: is_true(?a \notin [fset ?b]) |- _extend (a != b) (satr_nmemSgt _ a b)
  | _: is_true([fset ?a] `<=` ?A) |- _extend (a \in A) (satr_sgtSub a A)
  | _: is_true(?A == ?B) |- _extend_conj (A `<=` B) (B `<=` A) (satr_eqP A B)
  | _: is_true(?a \notin ?A `|` ?B) |- _extend_conj (a \notin A) (a \notin B) (satr_nmemU a A B)
  | _: is_true(?A \in (powerset ?B)) |- _extend (A `<=` B) (pow_mem A B)
  
  | _: is_true(?a \in ?A), _: is_true(?A `<=` ?B) |- _extend (a \in B) (satr_memSub a A B)
  | _: is_true(?a \notin ?A), _: is_true(?B `<=` ?A) |- _extend (a \notin B) (satr_nmemSup a A B)
  | _: is_true(?a == ?b), _: is_true(?b \in ?A) |- _extend (a \in A) (satr_eqMem a b A)
  | _: is_true(?a == ?b), _: is_true(?b == ?c) |- _extend (a == c) (satr_eqTrans _ a b c)
  
  | _: is_true(~~ (?A `<=` ?B)) |- _extend_conj_ex A B (satr_notSub A B)
  | _: is_true(?A \notin (powerset ?B)) |- _extend_conj_ex A B (pow_nmem A B)
  
  | _: is_true(?a \in sep ?A ?p) |- _extend_conj (a \in A) (p a) (subst_memSep a A p)
  end.

Ltac branching :=
  match goal with
  | _: is_true(?s || ?t) |- _extend_disj s t (prop_disj s t)
  | _: is_true(?s ==> ?t) |- _extend_disj (~~ s) t (prop_impl s t)
  | _: is_true(~~ (?s && ?t)) |- _extend_disj (~~ s) (~~ t) (prop_negConj s t)
  
  | _: is_true(?a \in ?A `|` ?B) |- _extend_disj (a \in A) (a \in B) (satr_memU a A B)
  | _: is_true(?A != ?B) |- _extend_disconj_ex A B (satr_neqP A B)
  
  | _: is_true(?a \notin sep ?A ?p) |- _extend_disj (a \notin A) (~~ (p a)) (subst_nmemSep a A p)
  end.

Ltac cutrules :=
  match goal with
  | _: context[?a], _: context[?A] |- _extend_disj (a \in A) (a \notin A) (cut_mem a A)
  | _: context[?A], _: context[?B] |- _extend_disj (A `<=` B) (~~(A `<=` B)) (cut_sub A B)
  | _: context[?A], _: context[?B] |- _extend_disj (A == B) (A != B) (cut_eq _ A B)
  end.

form control

Ltac reverywhere X :=
  repeat rewrite X;
  repeat match goal with
           | H: _ |- _rewrite X in H
         end.

Ltac genSubst :=
  repeat (
      let V := fresh "su" in
      match goal with
        | H: is_true(?x == ?y) |- _
          match goal with
            | _: x = y |- _fail 1
            | _move: (eqP H) ⇒ V
          end
        | x := _ |- _subst x
      end
    ); subst.

Ltac clearNonbools :=
  repeat(
      match goal with
      | H : _ |- _
        match type of H with
        | is_true _fail 1
        | _ = _fail 1
        | _clear H
        end
      end
    ).

preprocessing

Lemma semiDN (b:bool) : ¬ ~~ b b.
Proof. case: b_ //. Qed.

Lemma eq2bool (T : eqType) (a b : T) : a = b a == b.
Proof. moveH. by rewrite H. Qed.

Ltac oDN :=
  let V := fresh "H" in
  match goal with
  | |- is_true _apply semiDN; hnfV
  | |- _ = _apply:eqP; apply semiDN; hnfV
  | |- Falseidtac
  end.

Ltac eq2bool :=
  let V := fresh "H" in
  match goal with
  | H: _ = _ |- _move:(eq2bool _ H) ⇒ V
  | _idtac
  end.

Ltac preproc :=
  repeat (let V := fresh "H" in moveV);
  oDN;
  eq2bool;
  reverywhere compSetOps;
  clearNonbools.

automated saturation without cuts

Ltac core :=
  repeat(
      genSubst;
      repeat nonbranching;
      try closebranch;
      genSubst;
      try branching;
      try closebranch
    ).

User-level Tactics


Ltac fset_dec :=
  preproc;
  repeat(
      try closebranch;
      core;
      try cutrules
    ).

omits cut rules
Ltac fset_nocut := preproc; try closebranch; core.

Ltac invert X :=
  match X with
    | (?x, ?y)
      match x with
        | (?x0, ?x1)let z := invert (x0, (x1, y)) in constr:(z)
      end
    | _constr:(X)
  end.

unfolds all definitions given in a tuple before applying fset_dec
Ltac fset_decu X :=
  let Y := invert X in
  match Y with
    | (?x, ?xs)unfold x in *; fset_decu xs
    | ?xunfold x in *; fset_dec
  end.

omits cut rules and unfolds definitions
Ltac fset_nocutu X :=
  let Y := invert X in
  match Y with
    | (?x, ?xs)unfold x in *; fset_nocutu xs
    | ?xunfold x in *; fset_nocut
  end.