summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Stephane Glondu <steph@glondu.net>2010-11-29 23:30:48 +0100
committerGravatar Stephane Glondu <steph@glondu.net>2010-11-29 23:30:48 +0100
commit1aa8b6f6a876af22f538c869f022bc4ca5986b40 (patch)
tree037f0286a86b2bcee9bfa08f00112005a71e0e18
Imported Upstream version 0.1-r13244upstream/0.1-r13244
-rw-r--r--AAC.v689
-rw-r--r--COPYING674
-rw-r--r--COPYING.LESSER165
-rw-r--r--Instances.v150
-rw-r--r--LICENSE13
-rw-r--r--README.txt65
-rw-r--r--Tests.v373
-rw-r--r--Tutorial.v321
-rw-r--r--aac_rewrite.ml308
-rw-r--r--aac_rewrite.mli59
-rw-r--r--coq.ml212
-rw-r--r--coq.mli117
-rw-r--r--files.txt7
-rw-r--r--magic.txt4
-rwxr-xr-xmake_makefile49
-rw-r--r--matcher.ml980
-rw-r--r--matcher.mli192
-rw-r--r--theory.ml708
-rw-r--r--theory.mli219
19 files changed, 5305 insertions, 0 deletions
diff --git a/AAC.v b/AAC.v
new file mode 100644
index 0000000..bdbe1b0
--- /dev/null
+++ b/AAC.v
@@ -0,0 +1,689 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** * Theory file for the aac_rewrite tactic
+
+ We define several base classes to package associative and possibly
+ commutative operators, and define a data-type for reified (or
+ quoted) expressions (with morphisms).
+
+ We then define a reflexive decision procedure to decide the
+ equality of reified terms: first normalize reified terms, then
+ compare them. This allows us to close transitivity steps
+ automatically, in the [aac_rewrite] tactic.
+
+ We restrict ourselves to the case where all symbols operate on a
+ single fixed type. In particular, this means that we cannot handle
+ situations like
+
+ [H: forall x y, nat_of_pos (pos_of_nat (x) + y) + x = ....]
+
+ where one occurence of [+] operates on nat while the other one
+ operates on positive. *)
+
+Require Import Arith NArith.
+Require Import List.
+Require Import FMapPositive FMapFacts.
+Require Import RelationClasses Equality.
+Require Export Morphisms.
+
+Set Implicit Arguments.
+Unset Automatic Introduction.
+Open Scope signature_scope.
+
+(** * Environments for the reification process: we use positive maps to index elements *)
+
+Section sigma.
+ Definition sigma := PositiveMap.t.
+ Definition sigma_get A (null : A) (map : sigma A) (n : positive) : A :=
+ match PositiveMap.find n map with
+ | None => null
+ | Some x => x
+ end.
+ Definition sigma_add := @PositiveMap.add.
+ Definition sigma_empty := @PositiveMap.empty.
+End sigma.
+
+(** * Packaging structures *)
+
+(** ** Free symbols *)
+Module Sym.
+ Section t.
+ Context {X} {R : relation X} .
+
+ (** type of an arity *)
+ Fixpoint type_of (n: nat) :=
+ match n with
+ | O => X
+ | S n => X -> type_of n
+ end.
+
+ (** relation to be preserved at an arity *)
+ Fixpoint rel_of n : relation (type_of n) :=
+ match n with
+ | O => R
+ | S n => respectful R (rel_of n)
+ end.
+
+ (** a symbol package contains an arity,
+ a value of the corresponding type,
+ and a proof that the value is a proper morphism *)
+ Record pack : Type := mkPack {
+ ar : nat;
+ value : type_of ar;
+ morph : Proper (rel_of ar) value
+ }.
+
+ (** helper to build default values, when filling reification environments *)
+ Program Definition null (H: Equivalence R) (x : X) := mkPack 0 x _.
+
+ End t.
+
+End Sym.
+
+(** ** Associative commutative operations (commutative monoids)
+
+ See #<a href="Instances.html">Instances.v</a># for concrete instances of these classes.
+
+*)
+
+Class Op_AC (X:Type) (R: relation X) (plus: X -> X -> X) (zero:X) := {
+ plus_compat:> Proper (R ==> R ==> R) plus;
+ plus_neutral_left: forall x, R (plus zero x) x;
+ plus_assoc: forall x y z, R (plus x (plus y z)) (plus (plus x y) z);
+ plus_com: forall x y, R (plus x y) (plus y x)
+}.
+
+Module Sum.
+ Section t.
+ Context {X : Type} {R: relation X}.
+ Class pack : Type := mkPack {
+ plus : X -> X -> X;
+ zero : X;
+ opac :> @Op_AC X R plus zero
+ }.
+ End t.
+End Sum.
+
+
+
+(** ** Associative operations (monoids) *)
+Class Op_A (X:Type) (R:relation X) (dot: X -> X -> X) (one: X) := {
+ dot_compat:> Proper (R ==> R ==> R) dot;
+ dot_neutral_left: forall x, R (dot one x) x;
+ dot_neutral_right: forall x, R (dot x one) x;
+ dot_assoc: forall x y z, R (dot x (dot y z)) (dot (dot x y) z)
+}.
+
+
+Module Prd.
+ Section t.
+ Context {X : Type} {R: relation X}.
+ Class pack : Type := mkPack {
+ dot : X -> X -> X;
+ one : X;
+ opa :> @Op_A X R dot one
+ }.
+ End t.
+End Prd.
+
+
+(** an helper lemma to derive an A class out of an AC one (not
+ declared as an instance to avoid useless branches in typeclass
+ resolution) *)
+Lemma AC_A {X} {R:relation X} {EQ: Equivalence R} {plus} {zero} (op: Op_AC R plus zero): Op_A R plus zero.
+Proof.
+ split; try apply op.
+ intros. rewrite plus_com. apply op.
+Qed.
+
+
+
+(** * Utilities for the evaluation function *)
+Section copy.
+
+ Context {X} {R: relation X} {HR : @Equivalence X R} {plus} {zero} {op: @Op_AC X R plus zero}.
+
+ (* copy n x = x+...+x (n times) *)
+ Definition copy n x := Prect (fun _ => X) x (fun _ xn => plus x xn) n.
+
+ Lemma copy_plus : forall n m x, R (copy (n+m) x) (plus (copy n x) (copy m x)).
+ Proof.
+ unfold copy.
+ induction n using Pind; intros m x.
+ rewrite Prect_base. rewrite <- Pplus_one_succ_l. rewrite Prect_succ. reflexivity.
+ rewrite Pplus_succ_permute_l. rewrite 2Prect_succ. rewrite IHn. apply plus_assoc.
+ Qed.
+
+ Global Instance copy_compat n: Proper (R ==> R) (copy n).
+ Proof.
+ unfold copy.
+ induction n using Pind; intros x y H.
+ rewrite 2Prect_base. assumption.
+ rewrite 2Prect_succ. apply plus_compat; auto.
+ Qed.
+
+End copy.
+
+(** * Utilities for positive numbers
+ which we use as:
+ - indices for morphisms and symbols
+ - multiplicity of terms in sums
+ *)
+Notation idx := positive.
+
+Fixpoint eq_idx_bool i j :=
+ match i,j with
+ | xH, xH => true
+ | xO i, xO j => eq_idx_bool i j
+ | xI i, xI j => eq_idx_bool i j
+ | _, _ => false
+ end.
+
+Fixpoint idx_compare i j :=
+ match i,j with
+ | xH, xH => Eq
+ | xH, _ => Lt
+ | _, xH => Gt
+ | xO i, xO j => idx_compare i j
+ | xI i, xI j => idx_compare i j
+ | xI _, xO _ => Gt
+ | xO _, xI _ => Lt
+ end.
+
+Notation pos_compare := idx_compare (only parsing).
+
+(** specification predicate for boolean binary functions *)
+Inductive decide_spec {A} {B} (R : A -> B -> Prop) (x : A) (y : B) : bool -> Prop :=
+| decide_true : R x y -> decide_spec R x y true
+| decide_false : ~(R x y) -> decide_spec R x y false.
+
+Lemma eq_idx_spec : forall i j, decide_spec (@eq _) i j (eq_idx_bool i j).
+Proof.
+ induction i; destruct j; simpl; try (constructor; congruence).
+ case (IHi j); constructor; congruence.
+ case (IHi j); constructor; congruence.
+Qed.
+
+(** weak specification predicate for comparison functions: only the 'Eq' case is specified *)
+Inductive compare_weak_spec A: A -> A -> comparison -> Prop :=
+| pcws_eq: forall i, compare_weak_spec i i Eq
+| pcws_lt: forall i j, compare_weak_spec i j Lt
+| pcws_gt: forall i j, compare_weak_spec i j Gt.
+
+Lemma pos_compare_weak_spec: forall i j, compare_weak_spec i j (pos_compare i j).
+Proof. induction i; destruct j; simpl; try constructor; case (IHi j); intros; constructor. Qed.
+
+Lemma idx_compare_reflect_eq: forall i j, idx_compare i j = Eq -> i=j.
+Proof. intros i j. case (pos_compare_weak_spec i j); intros; congruence. Qed.
+
+
+(** * Dependent types utilities *)
+Notation cast T H u := (eq_rect _ T u _ H).
+
+Section dep.
+ Variable U: Type.
+ Variable T: U -> Type.
+
+ Lemma cast_eq: (forall u v: U, {u=v}+{u<>v}) ->
+ forall A (H: A=A) (u: T A), cast T H u = u.
+ Proof. intros. rewrite <- Eqdep_dec.eq_rect_eq_dec; trivial. Qed.
+
+ Variable f: forall A B, T A -> T B -> comparison.
+ Definition reflect_eqdep := forall A u B v (H: A=B), @f A B u v = Eq -> cast T H u = v.
+
+ (* these lemmas have to remain transparent to get structural recursion
+ in the lemma [tcompare_weak_spec] below *)
+ Lemma reflect_eqdep_eq: reflect_eqdep -> forall A u v, @f A A u v = Eq -> u = v.
+ Proof. intros H A u v He. apply (H _ _ _ _ eq_refl He). Defined.
+
+ Lemma reflect_eqdep_weak_spec: reflect_eqdep -> forall A u v, compare_weak_spec u v (@f A A u v).
+ Proof.
+ intros. case_eq (f u v); try constructor.
+ intro H'. apply reflect_eqdep_eq in H'. subst. constructor. assumption.
+ Defined.
+End dep.
+
+
+(** * Utilities about lists and multisets *)
+
+(** finite multisets are represented with ordered lists with multiplicities *)
+Definition mset A := list (A*positive).
+
+(** lexicographic composition of comparisons (this is a notation to keep it lazy) *)
+Notation lex e f := (match e with Eq => f | _ => e end).
+
+
+Section lists.
+
+ (** comparison function *)
+ Section c.
+ Variables A B: Type.
+ Variable compare: A -> B -> comparison.
+ Fixpoint list_compare h k :=
+ match h,k with
+ | nil, nil => Eq
+ | nil, _ => Lt
+ | _, nil => Gt
+ | u::h, v::k => lex (compare u v) (list_compare h k)
+ end.
+ End c.
+ Definition mset_compare A B compare: mset A -> mset B -> comparison :=
+ list_compare (fun un vm => let '(u,n) := un in let '(v,m) := vm in lex (compare u v) (pos_compare n m)).
+
+ Section list_compare_weak_spec.
+ Variable A: Type.
+ Variable compare: A -> A -> comparison.
+ Hypothesis Hcompare: forall u v, compare_weak_spec u v (compare u v).
+ (* this lemma has to remain transparent to get structural recursion
+ in the lemma [tcompare_weak_spec] below *)
+ Lemma list_compare_weak_spec: forall h k, compare_weak_spec h k (list_compare compare h k).
+ Proof.
+ induction h as [|u h IHh]; destruct k as [|v k]; simpl; try constructor.
+ case (Hcompare u v); try constructor.
+ case (IHh k); intros; constructor.
+ Defined.
+ End list_compare_weak_spec.
+
+ Section mset_compare_weak_spec.
+ Variable A: Type.
+ Variable compare: A -> A -> comparison.
+ Hypothesis Hcompare: forall u v, compare_weak_spec u v (compare u v).
+ (* this lemma has to remain transparent to get structural recursion
+ in the lemma [tcompare_weak_spec] below *)
+ Lemma mset_compare_weak_spec: forall h k, compare_weak_spec h k (mset_compare compare h k).
+ Proof.
+ apply list_compare_weak_spec.
+ intros [u n] [v m].
+ case (Hcompare u v); try constructor.
+ case (pos_compare_weak_spec n m); try constructor.
+ Defined.
+ End mset_compare_weak_spec.
+
+ (** (sorted) merging functions *)
+ Section m.
+ Variable A: Type.
+ Variable compare: A -> A -> comparison.
+
+ Fixpoint merge_msets l1 :=
+ match l1 with
+ | nil => fun l2 => l2
+ | (h1,n1)::t1 =>
+ let fix merge_aux l2 :=
+ match l2 with
+ | nil => l1
+ | (h2,n2)::t2 =>
+ match compare h1 h2 with
+ | Eq => (h1,Pplus n1 n2)::merge_msets t1 t2
+ | Lt => (h1,n1)::merge_msets t1 l2
+ | Gt => (h2,n2)::merge_aux t2
+ end
+ end
+ in merge_aux
+ end.
+
+ (** interpretation of a list with a constant and a binary operation *)
+ Variable B: Type.
+ Variable map: A -> B.
+ Variable b2: B -> B -> B.
+ Variable b0: B.
+ Fixpoint fold_map l :=
+ match l with
+ | nil => b0
+ | u::nil => map u
+ | u::l => b2 (map u) (fold_map l)
+ end.
+
+ (** mapping and merging *)
+ Variable merge: A -> list B -> list B.
+ Fixpoint merge_map (l: list A): list B :=
+ match l with
+ | nil => nil
+ | u::l => merge u (merge_map l)
+ end.
+
+ End m.
+
+End lists.
+
+
+
+(** * Reification, normalisation, and decision *)
+Section s.
+ Context {X} {R: relation X} {E: @Equivalence X R}.
+ Infix "==" := R (at level 80).
+
+ (* We use environments to store the various operators and the
+ morphisms.*)
+
+ Variable e_sym: idx -> @Sym.pack X R.
+ Variable e_prd: idx -> @Prd.pack X R.
+ Variable e_sum: idx -> @Sum.pack X R.
+ Hint Resolve e_sum e_prd: typeclass_instances.
+
+
+ (** ** Almost normalised syntax
+ a term in [T] is in normal form if:
+ - sums do not contain sums
+ - products do not contain products
+ - there are no unary sums or products
+ - lists and msets are lexicographically sorted according to the order we define below
+
+ [vT n] denotes the set of term vectors of size [n] (the mutual dependency could be removed),
+
+ Note that [T] and [vT] depend on the [e_sym] environment (which
+ contains, among other things, the arity of symbols)
+ *)
+ Inductive T: Type :=
+ | sum: idx -> mset T -> T
+ | prd: idx -> list T -> T
+ | sym: forall i, vT (Sym.ar (e_sym i)) -> T
+ with vT: nat -> Type :=
+ | vnil: vT O
+ | vcons: forall n, T -> vT n -> vT (S n).
+
+
+ (** lexicographic rpo over the normalised syntax *)
+ Fixpoint compare (u v: T) :=
+ match u,v with
+ | sum i l, sum j vs => lex (idx_compare i j) (mset_compare compare l vs)
+ | prd i l, prd j vs => lex (idx_compare i j) (list_compare compare l vs)
+ | sym i l, sym j vs => lex (idx_compare i j) (vcompare l vs)
+ | sum _ _, _ => Lt
+ | _ , sum _ _ => Gt
+ | prd _ _, _ => Lt
+ | _ , prd _ _ => Gt
+ end
+ with vcompare i j (us: vT i) (vs: vT j) :=
+ match us,vs with
+ | vnil, vnil => Eq
+ | vnil, _ => Lt
+ | _, vnil => Gt
+ | vcons _ u us, vcons _ v vs => lex (compare u v) (vcompare us vs)
+ end.
+
+
+ (** we need to show that compare reflects equality
+ (this is because we work with msets rather than lists with arities)
+ *)
+
+ Lemma tcompare_weak_spec: forall (u v : T), compare_weak_spec u v (compare u v)
+ with vcompare_reflect_eqdep: forall i us j vs (H: i=j), vcompare us vs = Eq -> cast vT H us = vs.
+ Proof.
+ induction u.
+ destruct v; simpl; try constructor.
+ case (pos_compare_weak_spec p p0); intros; try constructor.
+ case (mset_compare_weak_spec compare tcompare_weak_spec m m0); intros; try constructor.
+ destruct v; simpl; try constructor.
+ case (pos_compare_weak_spec p p0); intros; try constructor.
+ case (list_compare_weak_spec compare tcompare_weak_spec l l0); intros; try constructor.
+ destruct v0; simpl; try constructor.
+ case_eq (idx_compare i i0); intro Hi; try constructor.
+ apply idx_compare_reflect_eq in Hi. symmetry in Hi. subst. (* the [symmetry] is required ! *)
+ case_eq (vcompare v v0); intro Hv; try constructor.
+ rewrite <- (vcompare_reflect_eqdep _ _ _ _ eq_refl Hv). constructor.
+
+ induction us; destruct vs; simpl; intros H Huv; try discriminate.
+ apply cast_eq, eq_nat_dec.
+ injection H; intro Hn.
+ revert Huv; case (tcompare_weak_spec t t0); intros; try discriminate.
+ symmetry in Hn. subst. (* symmetry required *)
+ rewrite <- (IHus _ _ eq_refl Huv).
+ apply cast_eq, eq_nat_dec.
+ Qed.
+
+ (** ** Evaluation from syntax to the abstract domain *)
+ Fixpoint eval u: X :=
+ match u with
+ | sum i l => fold_map (fun un => let '(u,n):=un in @copy _ (@Sum.plus _ _ (e_sum i)) n (eval u))
+ (@Sum.plus _ _ (e_sum i)) (@Sum.zero _ _ (e_sum i)) l
+ | prd i l => fold_map eval
+ (@Prd.dot _ _ (e_prd i)) (@Prd.one _ _ (e_prd i)) l
+ | sym i v => @eval_aux _ v (Sym.value (e_sym i))
+ end
+ with eval_aux i (v: vT i): Sym.type_of i -> X :=
+ match v with
+ | vnil => fun f => f
+ | vcons _ u v => fun f => eval_aux v (f (eval u))
+ end.
+
+
+ Instance eval_aux_compat i (l: vT i): Proper (@Sym.rel_of X R i ==> R) (eval_aux l).
+ Proof.
+ induction l; simpl; repeat intro.
+ assumption.
+ apply IHl, H. reflexivity.
+ Qed.
+
+
+ (** ** Normalisation *)
+
+ (** auxiliary functions, to clean up sums and products *)
+ Definition sum' i (u: mset T): T :=
+ match u with
+ | (u,xH)::nil => u
+ | _ => sum i u
+ end.
+ Definition is_sum i (u: T): option (mset T) :=
+ match u with
+ | sum j l => if eq_idx_bool j i then Some l else None
+ | u => None
+ end.
+ Definition copy_mset n (l: mset T): mset T :=
+ match n with
+ | xH => l
+ | _ => List.map (fun vm => let '(v,m):=vm in (v,Pmult n m)) l
+ end.
+ Definition sum_to_mset i (u: T) n :=
+ match is_sum i u with
+ | Some l' => copy_mset n l'
+ | None => (u,n)::nil
+ end.
+
+ Definition prd' i (u: list T): T :=
+ match u with
+ | u::nil => u
+ | _ => prd i u
+ end.
+ Definition is_prd i (u: T): option (list T) :=
+ match u with
+ | prd j l => if eq_idx_bool j i then Some l else None
+ | u => None
+ end.
+ Definition prd_to_list i (u: T) :=
+ match is_prd i u with
+ | Some l' => l'
+ | None => u::nil
+ end.
+
+
+ (** we deduce the normalisation function *)
+ Fixpoint norm u :=
+ match u with
+ | sum i l => sum' i (merge_map (fun un l => let '(u,n):=un in
+ merge_msets compare (sum_to_mset i (norm u) n) l) l)
+ | prd i l => prd' i (merge_map (fun u l => prd_to_list i (norm u) ++ l) l)
+ | sym i l => sym i (vnorm l)
+ end
+ with vnorm i (l: vT i): vT i :=
+ match l with
+ | vnil => vnil
+ | vcons _ u l => vcons (norm u) (vnorm l)
+ end.
+
+
+ (** ** Correctness *)
+
+ (** auxiliary lemmas about sums *)
+ Lemma sum'_sum i (l: mset T): eval (sum' i l) ==eval (sum i l) .
+ Proof.
+ intros i [|? [|? ?]].
+ reflexivity.
+ destruct p; destruct p; simpl; reflexivity.
+ destruct p; destruct p; simpl; reflexivity.
+ Qed.
+
+ Lemma eval_sum_cons i n a (l: mset T):
+ (eval (sum i ((a,n)::l))) == (@Sum.plus _ _ (e_sum i) (@copy _ (@Sum.plus _ _ (e_sum i)) n (eval a)) (eval (sum i l))).
+ Proof.
+ intros i n a [|[b m] l]; simpl.
+ rewrite plus_com, plus_neutral_left. reflexivity.
+ reflexivity.
+ Qed.
+
+ Lemma eval_sum_app i (h k: mset T): eval (sum i (h++k)) == @Sum.plus _ _ (e_sum i) (eval (sum i h)) (eval (sum i k)).
+ Proof.
+ induction h; intro k.
+ simpl. rewrite plus_neutral_left. reflexivity.
+ simpl app. destruct a. rewrite 2 eval_sum_cons, IHh, plus_assoc. reflexivity.
+ Qed.
+
+ Lemma eval_merge_sum i (h k: mset T):
+ eval (sum i (merge_msets compare h k)) == @Sum.plus _ _ (e_sum i) (eval (sum i h)) (eval (sum i k)).
+ Proof.
+ induction h as [|[a n] h IHh]; intro k.
+ simpl. rewrite plus_neutral_left. reflexivity.
+ induction k as [|[b m] k IHk].
+ simpl. setoid_rewrite plus_com. rewrite plus_neutral_left. reflexivity.
+ simpl merge_msets. rewrite eval_sum_cons. destruct (tcompare_weak_spec a b) as [a|a b|a b].
+ rewrite 2eval_sum_cons. rewrite IHh. rewrite <- plus_assoc. setoid_rewrite plus_assoc at 3.
+ setoid_rewrite plus_com at 5. rewrite !plus_assoc. rewrite copy_plus. reflexivity.
+
+ rewrite eval_sum_cons. rewrite IHh. apply plus_assoc.
+
+ rewrite <- eval_sum_cons. setoid_rewrite eval_sum_cons at 3.
+ rewrite plus_com, <- plus_assoc. rewrite plus_com in IHk. rewrite <- IHk.
+ rewrite eval_sum_cons. apply plus_compat; reflexivity.
+ Qed.
+
+ Inductive sum_spec: T -> option (mset T) -> Prop :=
+ | ss_sum1: forall i l, sum_spec (sum i l) (Some l)
+ | ss_sum2: forall j i l, i<>j -> sum_spec (sum i l) None
+ | ss_prd: forall i l, sum_spec (prd i l) None
+ | ss_sym: forall i l, sum_spec (@sym i l) None.
+ Lemma is_sum_spec: forall i (a: T), sum_spec a (is_sum i a).
+ Proof.
+ intros i [j l|j l|j l]; simpl; try constructor.
+ case eq_idx_spec; intro.
+ subst. constructor.
+ econstructor. eassumption.
+ Qed.
+
+ Lemma eval_is_sum: forall i (a: T) k, is_sum i a = Some k -> eval (sum i k) = eval a.
+ Proof.
+ intros i [j l|j l|j l] k; simpl is_sum; try (intro; discriminate).
+ case eq_idx_spec; intros ? H.
+ injection H. clear H. intros <-. congruence.
+ discriminate.
+ Qed.
+
+ Lemma copy_mset' n (l: mset T):
+ copy_mset n l = List.map (fun vm => let '(v,m):=vm in (v,Pmult n m)) l.
+ Proof.
+ intros. destruct n; try reflexivity.
+ simpl. induction l as [|[a l] IHl]; simpl; congruence.
+ Qed.
+
+
+ Lemma copy_mset_succ i n (l: mset T):
+ eval (sum i (copy_mset (Psucc n) l)) == @Sum.plus _ _ (e_sum i) (eval (sum i l)) (eval (sum i (copy_mset n l))).
+ Proof.
+ intros. rewrite 2copy_mset'.
+ induction l as [|[a m] l IHl].
+ simpl eval at 2. rewrite plus_neutral_left. destruct n; reflexivity.
+ simpl map. rewrite ! eval_sum_cons. rewrite IHl.
+ rewrite Pmult_Sn_m. rewrite copy_plus. rewrite <- !plus_assoc. apply plus_compat; try reflexivity.
+ setoid_rewrite plus_com at 3. rewrite <- !plus_assoc. apply plus_compat; try reflexivity. apply plus_com.
+ Qed.
+
+ Lemma eval_sum_to_mset: forall i n a, eval (sum i (sum_to_mset i a n)) == @copy _ (@Sum.plus _ _ (e_sum i)) n (eval a).
+ Proof.
+ intros. unfold sum_to_mset.
+ case_eq (is_sum i a).
+ intros k Hk. induction n using Pind.
+ simpl copy_mset. rewrite (eval_is_sum _ _ Hk). reflexivity.
+ rewrite copy_mset_succ. rewrite IHn. rewrite (eval_is_sum _ _ Hk).
+ unfold copy at 2. rewrite Prect_succ. reflexivity.
+ reflexivity.
+ Qed.
+
+
+ (** auxiliary lemmas about products *)
+ Lemma prd'_prd i (l: list T): eval (prd' i l) == eval (prd i l).
+ Proof. intros i [|? [|? ?]]; reflexivity. Qed.
+
+ Lemma eval_prd_cons i a (l: list T): eval (prd i (a::l)) == @Prd.dot _ _ (e_prd i) (eval a) (eval (prd i l)).
+ Proof.
+ intros i a [|b l]; simpl.
+ rewrite dot_neutral_right. reflexivity.
+ reflexivity.
+ Qed.
+
+ Lemma eval_prd_app i (h k: list T): eval (prd i (h++k)) == @Prd.dot _ _ (e_prd i) (eval (prd i h)) (eval (prd i k)).
+ Proof.
+ induction h; intro k.
+ simpl. rewrite dot_neutral_left. reflexivity.
+ simpl app. rewrite 2 eval_prd_cons, IHh, dot_assoc. reflexivity.
+ Qed.
+
+ Lemma eval_is_prd: forall i (a: T) k, is_prd i a = Some k -> eval (prd i k) = eval a.
+ Proof.
+ intros i [j l|j l|j l] k; simpl is_prd; try (intro; discriminate).
+ case eq_idx_spec; intros ? H.
+ injection H. clear H. intros <-. subst. congruence.
+ discriminate.
+ Qed.
+
+ Lemma eval_prd_to_list: forall i a, eval (prd i (prd_to_list i a)) = eval a.
+ Proof.
+ intros. unfold prd_to_list.
+ case_eq (is_prd i a).
+ intros k Hk. apply eval_is_prd. assumption.
+ reflexivity.
+ Qed.
+
+
+ (** correctness of the normalisation function *)
+ Theorem eval_norm: forall u, eval (norm u) == eval u
+ with eval_norm_aux: forall i (l: vT i) (f: Sym.type_of i),
+ Proper (@Sym.rel_of X R i) f -> eval_aux (vnorm l) f == eval_aux l f.
+ Proof.
+ induction u.
+ simpl norm. rewrite sum'_sum. induction m as [|[u n] m IHm].
+ reflexivity.
+ simpl merge_map. rewrite eval_merge_sum.
+ rewrite eval_sum_to_mset, IHm, eval_norm.
+ rewrite eval_sum_cons. reflexivity.
+
+ simpl norm. rewrite prd'_prd. induction l.
+ reflexivity.
+ simpl merge_map.
+ rewrite eval_prd_app.
+ rewrite eval_prd_to_list, IHl, eval_norm.
+ rewrite eval_prd_cons. reflexivity.
+
+ simpl. apply eval_norm_aux, Sym.morph.
+
+
+ induction l; simpl; intros f Hf.
+ reflexivity.
+ rewrite eval_norm. apply IHl, Hf. reflexivity.
+ Qed.
+
+ (** corollaries, for goal normalisation or decision *)
+ Lemma normalise: forall (u v: T), eval (norm u) == eval (norm v) -> eval u == eval v.
+ Proof. intros u v. rewrite 2 eval_norm. trivial. Qed.
+
+ Lemma compare_reflect_eq: forall u v, compare u v = Eq -> eval u == eval v.
+ Proof. intros u v. case (tcompare_weak_spec u v); intros; try congruence. reflexivity. Qed.
+
+ Lemma decide: forall (u v: T), compare (norm u) (norm v) = Eq -> eval u == eval v.
+ Proof. intros u v H. apply normalise. apply compare_reflect_eq. apply H. Qed.
+
+End s.
+Declare ML Module "aac_tactics".
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/COPYING.LESSER b/COPYING.LESSER
new file mode 100644
index 0000000..cca7fc2
--- /dev/null
+++ b/COPYING.LESSER
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/Instances.v b/Instances.v
new file mode 100644
index 0000000..1a2e08f
--- /dev/null
+++ b/Instances.v
@@ -0,0 +1,150 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** Instances for aac_rewrite.*)
+
+(* At the moment, all the instances are exported even if they are packaged into modules. *)
+
+Require Export AAC.
+
+Module Peano.
+ Require Import Arith NArith Max.
+ Program Instance aac_plus : @Op_AC nat eq plus 0 := @Build_Op_AC nat (@eq nat) plus 0 _ plus_0_l plus_assoc plus_comm.
+
+
+ Program Instance aac_mult : Op_AC eq mult 1 := Build_Op_AC _ _ _ mult_assoc mult_comm.
+ (* We also declare a default associative operation: this is currently
+ required to fill reification environments *)
+ Definition default_a := AC_A aac_plus. Existing Instance default_a.
+End Peano.
+
+Module Z.
+ Require Import ZArith.
+ Open Scope Z_scope.
+ Program Instance aac_plus : Op_AC eq Zplus 0 := Build_Op_AC _ _ _ Zplus_assoc Zplus_comm.
+ Program Instance aac_mult : Op_AC eq Zmult 1 := Build_Op_AC _ _ Zmult_1_l Zmult_assoc Zmult_comm.
+ Definition default_a := AC_A aac_plus. Existing Instance default_a.
+End Z.
+
+Module Q.
+ Require Import QArith.
+ Program Instance aac_plus : Op_AC Qeq Qplus 0 := Build_Op_AC _ _ Qplus_0_l Qplus_assoc Qplus_comm.
+ Program Instance aac_mult : Op_AC Qeq Qmult 1 := Build_Op_AC _ _ Qmult_1_l Qmult_assoc Qmult_comm.
+ Definition default_a := AC_A aac_plus. Existing Instance default_a.
+End Q.
+
+Module Prop_ops.
+ Program Instance aac_or : Op_AC iff or False. Solve All Obligations using tauto.
+
+ Program Instance aac_and : Op_AC iff and True. Solve All Obligations using tauto.
+
+ Definition default_a := AC_A aac_and. Existing Instance default_a.
+
+ Program Instance aac_not_compat : Proper (iff ==> iff) not.
+ Solve All Obligations using firstorder.
+End Prop_ops.
+
+Module Bool.
+ Program Instance aac_orb : Op_AC eq orb false.
+ Solve All Obligations using firstorder.
+
+ Program Instance aac_andb : Op_AC eq andb true.
+ Solve All Obligations using firstorder.
+
+ Definition default_a := AC_A aac_andb. Existing Instance default_a.
+
+ Instance negb_compat : Proper (eq ==> eq) negb.
+ Proof. intros [|] [|]; auto. Qed.
+End Bool.
+
+Module Relations.
+ Require Import Relations.
+ Section defs.
+ Variable T : Type.
+ Variables R S: relation T.
+ Definition inter : relation T := fun x y => R x y /\ S x y.
+ Definition compo : relation T := fun x y => exists z : T, R x z /\ S z y.
+ Definition negr : relation T := fun x y => ~ R x y.
+ (* union and converse are already defined in the standard library *)
+
+ Definition bot : relation T := fun _ _ => False.
+ Definition top : relation T := fun _ _ => True.
+ End defs.
+
+ Program Instance aac_union T : Op_AC (same_relation T) (union T) (bot T).
+ Solve All Obligations using compute; [tauto || intuition].
+
+ Program Instance aac_inter T : Op_AC (same_relation T) (inter T) (top T).
+ Solve All Obligations using compute; firstorder.
+
+ (* note that we use [eq] directly as a neutral element for composition *)
+ Program Instance aac_compo T : Op_A (same_relation T) (compo T) eq.
+ Solve All Obligations using compute; firstorder.
+ Solve All Obligations using compute; firstorder subst; trivial.
+
+ Instance negr_compat T : Proper (same_relation T ==> same_relation T) (negr T).
+ Proof. compute. firstorder. Qed.
+
+ Instance transp_compat T : Proper (same_relation T ==> same_relation T) (transp T).
+ Proof. compute. firstorder. Qed.
+
+ Instance clos_trans_incr T : Proper (inclusion T ==> inclusion T) (clos_trans T).
+ Proof.
+ intros R S H x y Hxy. induction Hxy.
+ constructor 1. apply H. assumption.
+ econstructor 2; eauto 3.
+ Qed.
+ Instance clos_trans_compat T : Proper (same_relation T ==> same_relation T) (clos_trans T).
+ Proof. intros R S H; split; apply clos_trans_incr, H. Qed.
+
+ Instance clos_refl_trans_incr T : Proper (inclusion T ==> inclusion T) (clos_refl_trans T).
+ Proof.
+ intros R S H x y Hxy. induction Hxy.
+ constructor 1. apply H. assumption.
+ constructor 2.
+ econstructor 3; eauto 3.
+ Qed.
+ Instance clos_refl_trans_compat T : Proper (same_relation T ==> same_relation T) (clos_refl_trans T).
+ Proof. intros R S H; split; apply clos_refl_trans_incr, H. Qed.
+
+End Relations.
+
+Module All.
+ Export Peano.
+ Export Z.
+ Export Prop_ops.
+ Export Bool.
+ Export Relations.
+End All.
+
+(* A small test to ensure that everything can live together *)
+(* Section test.
+ Import All.
+ Require Import ZArith.
+ Open Scope Z_scope.
+ Notation "x ^2" := (x*x) (at level 40).
+ Hypothesis H : forall x y, x^2 + y^2 + x*y + x* y = (x+y)^2.
+ Goal forall a b c, a*1*(a ^2)*a + c + (b+0)*1*b + a^2*b + a*b*a = (a^2+b)^2+c.
+ intros.
+ aac_rewrite H.
+ aac_rewrite <- H .
+ symmetry.
+ aac_rewrite <- H .
+ aac_reflexivity.
+ Qed.
+ Open Scope nat_scope.
+ Notation "x ^^2" := (x * x) (at level 40).
+ Hypothesis H' : forall (x y : nat), x^^2 + y^^2 + x*y + x* y = (x+y)^^2.
+
+ Goal forall (a b c : nat), a*1*(a ^^2)*a + c + (b+0)*1*b + a^^2*b + a*b*a = (a^^2+b)^^2+c.
+ intros. aac_rewrite H'. aac_reflexivity.
+ Qed.
+End test.
+
+*)
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1be0ca5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+The aac_tactics plugin library is free software: you can redistribute
+it and/or modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation, either version 3
+of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see
+<http://www.gnu.org/licenses/>.
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..ad3b5c9
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,65 @@
+
+ aac_tactics
+ ===========
+
+ Thomas Braibant & Damien Pous
+
+Laboratoire d'Informatique de Grenoble (UMR 5217), INRIA, CNRS, France
+
+Webpage of the project: http://sardes.inrialpes.fr/~braibant/aac_tactics/
+
+
+FOREWORD
+========
+
+This plugin provides tactics for rewriting universally quantified
+equations, modulo associativity and commutativity of some operators.
+
+INSTALL
+=======
+
+- run [./make_makefile] in the top-level directory to generate a Makefile
+- run [make world] to build the plugin and the documentation
+
+This should work with Coq v8.3 `beta' : -r 13027, -r 13060 and hopefully the
+following ones.
+
+To be able to import the plugin from anywhere, add the following to
+your ~/.coqrc
+
+Add Rec LoadPath "path_to_aac_tactics".
+Add ML Path "path_to_aac_tactics".
+
+
+DOCUMENTATION
+=============
+
+Please refer to Tutorial.v for a succint introduction on how to use
+this plugin.
+
+To understand the inner-working of the tactic, please refer to the
+.mli files as the main source of information on each .ml
+file. Alternatively, [make world] generates ocamldoc/coqdoc
+documentation in directories doc/ and html/, respectively.
+
+File Instances.v defines several instances for frequent use-cases of
+this plugin, that should allow you to use it out-of-the-shelf. Namely,
+we have instances for:
+
+- Peano naturals (Import Instances.Peano)
+- Z binary numbers (Import Instances.Z)
+- Rationnal numbers (Import Instances.Q)
+- Prop (Import Instances.Prop_ops)
+- Booleans (Import Instances.Bool)
+- Relations (Import Instances.Relations)
+- All of the above (Import Instances.All)
+
+
+ACKNOWLEDGEMENTS
+================
+
+We are grateful to Evelyne Contejean, Hugo Herbelin, Assia Mahboubi
+and Matthieu Sozeau for highly instructive discussions.
+
+This plugin took inspiration from the plugin tutorial "constructors",
+distributed under the LGPL 2.1, copyrighted by Matthieu Sozeau
diff --git a/Tests.v b/Tests.v
new file mode 100644
index 0000000..87cc121
--- /dev/null
+++ b/Tests.v
@@ -0,0 +1,373 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+Require Import AAC.
+
+Section sanity.
+
+ Context {X} {R} {E: Equivalence R} {plus} {zero}
+ {dot} {one} {A: @Op_A X R dot one} {AC: Op_AC R plus zero}.
+
+
+ Notation "x == y" := (R x y) (at level 70).
+ Notation "x * y" := (dot x y) (at level 40, left associativity).
+ Notation "1" := (one).
+ Notation "x + y" := (plus x y) (at level 50, left associativity).
+ Notation "0" := (zero).
+
+ Section t0.
+ Hypothesis H : forall x, x+x == x.
+ Goal forall a b, a+b +a == a+b.
+ intros a b; aacu_rewrite H; aac_reflexivity.
+ Qed.
+ End t0.
+
+ Section t1.
+ Variables a b : X.
+
+ Variable P : X -> Prop.
+ Hypothesis H : forall x y, P y -> x+y+x == x.
+ Hypothesis Hb : P b.
+ Goal a+b +a == a.
+ aacu_rewrite H.
+ aac_reflexivity.
+ auto.
+ Qed.
+ End t1.
+
+ Section t.
+ Variable f : X -> X.
+ Hypothesis Hf : Proper (R ==> R) f.
+ Hypothesis H : forall x, x+x == x.
+ Goal forall y, f(y+y) == f (y).
+ intros y.
+ aacu_instances H.
+ aacu_rewrite H.
+ aac_reflexivity.
+ Qed.
+
+ Goal forall y z, f(y+z+z+y) == f (y+z).
+ intros y z.
+ aacu_instances H.
+ aacu_rewrite H.
+ aac_reflexivity.
+ Qed.
+
+ Hypothesis H' : forall x, x*x == x.
+ Goal forall y z, f(y*z*y*z) == f (y*z).
+ intros y z.
+ aacu_instances H'.
+ aacu_rewrite H'.
+ aac_reflexivity.
+ Qed.
+ End t.
+
+ Goal forall x y, (plus x y) == (plus y x). intros.
+ aac_reflexivity.
+ Qed.
+
+ Goal forall x y, R (plus (dot x one) y) (plus y x). intros.
+ aac_reflexivity.
+ Qed.
+
+ Goal (forall f y, f y + f y == y) -> forall a b g (Hg: Proper (R ==> R) g), g (a+b) + g b + g (b+a) == a + b + g b.
+ intros.
+ try aacu_rewrite H.
+ aacu_rewrite (H g).
+ Restart.
+ intros.
+ set (H' := H g).
+ aacu_rewrite H'.
+ aac_reflexivity.
+ Qed.
+
+ Section t2.
+ Variables a b: X.
+ Variables g : X ->X.
+ Hypothesis Hg:Proper (R ==> R) g.
+ Hypothesis H: (forall y, (g y) + y == y).
+ Goal g (a+b) + b +a+a == a + b +a.
+ intros.
+ aacu_rewrite H.
+ aac_reflexivity.
+ Qed.
+ End t2.
+ Section t3.
+ Variables a b: X.
+ Variables g : X ->X.
+ Hypothesis Hg:Proper (R ==> R) g.
+ Hypothesis H: (forall f y, f y + y == y).
+
+ Goal g (a+b) + b +a+a == a + b +a.
+ intros.
+ aacu_rewrite (H g).
+ aac_reflexivity.
+ Qed.
+End t3.
+
+Section null.
+ Hypothesis dot_ann : forall x, 0 * x == 0.
+ Goal forall a, 0*a == 0.
+ intros a. aac_rewrite dot_ann. reflexivity.
+ Qed.
+End null.
+
+End sanity.
+
+Section abs.
+ Context
+ {X} {E: Equivalence (@eq X)}
+ {plus} {zero} {AC: @Op_AC X eq plus zero}
+ {plus'} {zero'} {AC': @Op_AC X eq plus' zero'}
+ {dot} {one} {A: @Op_A X eq dot one}
+ {dot'} {one'} {A': @Op_A X eq dot' one'}.
+
+ Notation "x * y" := (dot x y) (at level 40, left associativity).
+ Notation "x @ y" := (dot' x y) (at level 20, left associativity).
+ Notation "1" := (one).
+ Notation "1'" := (one').
+ Notation "x + y" := (plus x y) (at level 50, left associativity).
+ Notation "x ^ y" := (plus' x y) (at level 30, right associativity).
+ Notation "0" := (zero).
+ Notation "0'" := (zero').
+
+ Variables a b c d e k: X.
+ Variables f g: X -> X.
+ Hypothesis Hf: Proper (eq ==> eq) f.
+ Hypothesis Hg: Proper (eq ==> eq) g.
+
+ Variables h: X -> X -> X.
+ Hypothesis Hh: Proper (eq ==> eq ==> eq) h.
+
+ Variables q: X -> X -> X -> X.
+ Hypothesis Hq: Proper (eq ==> eq ==> eq ==> eq) q.
+
+
+
+
+ Hypothesis dot_ann_left: forall x, 0*x = 0.
+
+ Goal f (a*0*b*c*d) = k.
+ aac_rewrite dot_ann_left.
+ Restart.
+ aac_rewrite dot_ann_left subterm 1 subst 0.
+ Abort.
+
+
+
+
+ Hypothesis distr_dp: forall x y z, x*(y+z) = x*y+x*z.
+ Hypothesis distr_dp': forall x y z, x*y+x*z = x*(y+z).
+
+ Hypothesis distr_pp: forall x y z, x^(y+z) = x^y+x^z.
+ Hypothesis distr_pp': forall x y z, x^y+x^z = x^(y+z).
+
+ Hypothesis distr_dd: forall x y z, x@(y*z) = x@y * x@z.
+ Hypothesis distr_dd': forall x y z, x@y * x@z = x@(y*z).
+
+
+ Goal a*b*(c+d+e) = k.
+ aac_instances distr_dp.
+ aacu_instances distr_dp.
+ aac_rewrite distr_dp subterm 0 subst 4.
+
+ aac_instances distr_dp'.
+ aac_rewrite distr_dp'.
+ Abort.
+
+ Goal a@(b*c)@d@(e*e) = k.
+ aacu_instances distr_dd.
+ aac_instances distr_dd.
+ aac_rewrite distr_dd.
+ Abort.
+
+ Goal a^(b+c)^d^(e+e) = k.
+ aacu_instances distr_dd.
+ aac_instances distr_dd.
+ aacu_instances distr_pp.
+ aac_instances distr_pp.
+ aac_rewrite distr_pp subterm 9.
+ Abort.
+
+
+
+
+ Hypothesis plus_idem: forall x, x+x = x.
+ Hypothesis plus_idem3: forall x, x+x+x = x.
+ Hypothesis dot_idem: forall x, x*x = x.
+ Hypothesis dot_idem3: forall x, x*x*x = x.
+
+ Goal a*b*a*b = k.
+ aac_instances dot_idem.
+ aacu_instances dot_idem.
+ aac_rewrite dot_idem.
+ aac_instances distr_dp. (* TODO: no solution -> fail ? *)
+ aacu_instances distr_dp.
+ Abort.
+
+ Goal a+a+a+a = k.
+ aac_instances plus_idem3.
+ aac_rewrite plus_idem3.
+ Abort.
+
+ Goal a*a*a*a = k.
+ aac_instances dot_idem3.
+ aac_rewrite dot_idem3.
+ Abort.
+
+ Goal a*a*a*a*a*a = k.
+ aac_instances dot_idem3.
+ aac_rewrite dot_idem3.
+ Abort.
+
+ Goal f(a*a)*f(a*a) = k.
+ aac_instances dot_idem.
+ (* TODO: pas clair qu'on doive réécrire les deux petits
+ sous-termes quand on nous demande de n'en réécrire qu'un...
+ d'autant plus que le comportement est nécessairement
+ imprévisible (cf. les deux exemples en dessous)
+ ceci dit, je suis conscient que c'est relou à empêcher... *)
+ aac_rewrite dot_idem subterm 1.
+ Abort.
+
+ Goal f(a*a*b)*f(a*a*b) = k.
+ aac_instances dot_idem.
+ aac_rewrite dot_idem subterm 2. (* une seule instance réécrite *)
+ Abort.
+
+ Goal f(b*a*a)*f(b*a*a) = k.
+ aac_instances dot_idem.
+ aac_rewrite dot_idem subterm 2. (* deux instances réécrites *)
+ Abort.
+
+ Goal h (a*a) (a*b*c*d*e) = k.
+ aac_rewrite dot_idem.
+ Abort.
+
+ Goal h (a+a) (a+b+c+d+e) = k.
+ aac_rewrite plus_idem.
+ Abort.
+
+ Goal (a*a+a*a+b)*c = k.
+ aac_rewrite plus_idem.
+ Restart.
+ aac_rewrite dot_idem.
+ Abort.
+
+
+
+
+ Hypothesis dot_inv: forall x, x*f x = 1.
+ Hypothesis plus_inv: forall x, x+f x = 0.
+ Hypothesis dot_inv': forall x, f x*x = 1.
+
+ Goal a*f(1)*b = k.
+ try aac_rewrite dot_inv. (* TODO: à terme, j'imagine qu'on souhaite accepter ça, même en aac *)
+ aacu_rewrite dot_inv.
+ Abort.
+
+ Goal f(1) = k.
+ aacu_rewrite dot_inv.
+ Restart.
+ aacu_rewrite dot_inv'.
+ Abort.
+
+ Goal b*f(b) = k.
+ aac_rewrite dot_inv.
+ Abort.
+
+ Goal f(b)*b = k.
+ aac_rewrite dot_inv'.
+ Abort.
+
+ Goal a*f(a*b)*a*b*c = k.
+ aac_rewrite dot_inv'.
+ Abort.
+
+ Goal g (a*f(a*b)*a*b*c+d) = k.
+ aac_rewrite dot_inv'.
+ Abort.
+
+ Goal g (a+f(a+b)+c+b+b) = k.
+ aac_rewrite plus_inv.
+ Abort.
+
+ Goal g (a+f(0)+c) = k.
+ aac_rewrite plus_inv.
+ Abort.
+
+
+
+
+
+ Goal forall R S, Equivalence R -> Equivalence S -> R a k -> S a k.
+ intros R S HR HS H.
+ try (aac_rewrite H ; fail 1 "ne doit pas passer").
+ try (aac_instances H; fail 1 "ne doit pas passer").
+ Abort.
+
+ Goal forall R S, Equivalence R -> Equivalence S -> (forall x, R (f x) k) -> S (f a) k.
+ intros R S HR HS H.
+ try (aac_instances H; fail 1 "ne doit pas passer").
+ try (aac_rewrite H ; fail 1 "ne doit pas passer").
+ Abort.
+
+
+
+
+
+ Hypothesis star_f: forall x y, x*f(y*x) = f(x*y)*x.
+
+ Goal g (a+b*c*d*f(a*b*c*d)+b) = k.
+ aac_instances star_f.
+ aac_rewrite star_f.
+ Abort.
+
+ Goal b*f(a*b)*f(b*b*f(a*b)) = k.
+ aac_instances star_f.
+ aac_rewrite star_f.
+ aac_rewrite star_f.
+ Abort.
+
+
+
+
+
+ Hypothesis dot_select: forall x y, f x * y * g x = g y*x*f y.
+
+ Goal f(a+b)*c*g(b+a) = k.
+ aac_rewrite dot_select.
+ Abort.
+
+ Goal f(a+b)*g(b+a) = k.
+ try (aac_rewrite dot_select; fail 1 "ne doit pas passer").
+ aacu_rewrite dot_select.
+ Abort.
+
+ Goal f b*f(a+b)*g(b+a)*g b = k.
+ aacu_instances dot_select.
+ aac_instances dot_select.
+ aac_rewrite dot_select.
+ aacu_rewrite dot_select.
+ aacu_rewrite dot_select.
+ Abort.
+
+
+
+
+ Hypothesis galois_dot: forall x y z, h x (y*z) = h (x*y) z.
+ Hypothesis galois_plus: forall x y z, h x (y+z) = h (x+y) z.
+
+ Hypothesis select_dot: forall x y, h x (y*x) = h (x*y) x.
+ Hypothesis select_plus: forall x y, h x (y+x) = h (x+y) x.
+
+ Hypothesis h_dot: forall x y, h (x*y) (y*x) = h x y.
+ Hypothesis h_plus: forall x y, h (x+y) (y+x) = h x y.
+
+End abs.
+
diff --git a/Tutorial.v b/Tutorial.v
new file mode 100644
index 0000000..84d40fc
--- /dev/null
+++ b/Tutorial.v
@@ -0,0 +1,321 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** * Examples about uses of the aac_rewrite library *)
+
+(*
+ Depending on your installation, either uncomment the following two
+ lines, or add them to your .coqrc files, replacing path_to_aac_tactics
+ with the correct path for your installation:
+
+ Add Rec LoadPath "path_to_aac_tactics".
+ Add ML Path "path_to_aac_tactics".
+*)
+Require Export AAC.
+Require Instances.
+
+(** ** Introductory examples *)
+
+(** First, we settle in the context of Z, and show an usage of our
+tactics: we rewrite an universally quantified hypothesis modulo
+associativity and commutativity. *)
+
+Section introduction.
+ Import ZArith.
+ Import Instances.Z.
+
+ Variables a b c : Z.
+ Hypothesis H: forall x, x + Zopp x = 0.
+ Goal a + b + c + Zopp (c + a) = b.
+ aac_rewrite H.
+ aac_reflexivity.
+ Qed.
+ Goal a + c+ Zopp (b + a + Zopp b) = c.
+ do 2 aac_rewrite H.
+ reflexivity.
+ Qed.
+
+ (** Notes:
+ - the tactic handles arbitrary function symbols like [Zopp] (as
+ long as they are proper morphisms);
+ - here, ring would have done the job.
+ *)
+
+End introduction.
+
+(** Second, we show how to exploit binomial identities to prove a goal
+about pythagorean triples, without breaking a sweat. By comparison,
+even if the goal and the hypothesis are both in normal form, making
+the rewrites using standard tools is difficult.
+*)
+
+Section binomials.
+
+ Import ZArith.
+ Import Instances.Z.
+
+ Notation "x ^2" := (x*x) (at level 40).
+ Notation "2 â‹… x" := (x+x) (at level 41).
+ Lemma Hbin1: forall x y, (x+y)^2 = x^2 + y^2 + 2â‹…x*y. Proof. intros; ring. Qed.
+ Lemma Hbin2: forall x y, x^2 + y^2 = (x+y)^2 + -(2â‹…x*y). Proof. intros; ring. Qed.
+ Lemma Hopp : forall x, x + -x = 0. Proof Zplus_opp_r.
+
+ Variables a b c : Z.
+ Hypothesis H : c^2 + 2â‹…(a+1)*b = (a+1+b)^2.
+ Goal a^2 + b^2 + 2â‹…a + 1 = c^2.
+ aacu_rewrite <- Hbin1.
+ aac_rewrite Hbin2.
+ aac_rewrite <- H.
+ aac_rewrite Hopp.
+ aac_reflexivity.
+ Qed.
+
+ (** Note: after the [aac_rewrite <- H], one could use [ring] to close the proof.*)
+
+End binomials.
+
+(** ** Usage *)
+
+(** One can also work in an abstract context, with arbitrary
+ associative and commutative operators.
+
+ (Note that one can declare several operations of each kind;
+ however, to be able to use this plugin, one currently needs at least
+ one associative operator, and one associative-commutative
+ operator.) *)
+
+Section base.
+
+ Context {X} {R} {E: Equivalence R}
+ {plus} {zero}
+ {dot} {one}
+ {A: @Op_A X R dot one}
+ {AC: Op_AC R plus zero}.
+
+ Notation "x == y" := (R x y) (at level 70).
+ Notation "x * y" := (dot x y) (at level 40, left associativity).
+ Notation "1" := (one).
+ Notation "x + y" := (plus x y) (at level 50, left associativity).
+ Notation "0" := (zero).
+
+ (** In the very first example, [ring] would have solved the
+ goal. Here, since [dot] does not necessarily distribute over [plus],
+ it is not possible to rely on it. *)
+
+ Section reminder.
+ Hypothesis H : forall x, x * x == x.
+ Variables a b c : X.
+
+ Goal (a+b+c)*(c+a+b) == a+b+c.
+ aac_rewrite H.
+ aac_reflexivity.
+ Qed.
+
+ (** Note: the tactic starts by normalizing terms, so that trailing
+ units are always eliminated. *)
+
+ Goal ((a+b)+0+c)*((c+a)+b*1) == a+b+c.
+ aac_rewrite H.
+ aac_reflexivity.
+ Qed.
+ End reminder.
+
+ (** We can deal with "proper" morphisms of arbitrary arity (here [f],
+ or [Zopp] earlier), and rewrite under morphisms (here [g]). *)
+
+ Section morphisms.
+ Variable f : X -> X -> X.
+ Hypothesis Hf : Proper (R ==> R ==> R) f.
+ Variable g : X -> X.
+ Hypothesis Hg : Proper (R ==> R) g.
+
+ Variable a b: X.
+ Hypothesis H : forall x y, x+f (b+y) x == y+x.
+ Goal g ((f (a+b) a) + a) == g (a+a).
+ aac_rewrite H.
+ reflexivity.
+ Qed.
+ End morphisms.
+
+ (** ** Selecting what and where to rewrite *)
+
+ (** There are sometimes several possible rewriting. We now show how
+ to interact with the tactic to select the desired one. *)
+
+ Section occurrence.
+ Variable f : X -> X .
+ Variable a : X.
+ Hypothesis Hf : Proper (R ==> R) f.
+ Hypothesis H : forall x, x + x == x.
+
+ Goal f(a+a)+f(a+a) == f a.
+ (** In case there are several possible solutions, one can print
+ the different solutions using the [aac_instances] tactic (in
+ proofgeneral, look at buffer *coq* ): *)
+ aac_instances H.
+ (** the default choice is the smallest possible context (number
+ 0), but one can choose the desired context; *)
+ aac_rewrite H subterm 1.
+ (** now the goal is [f a + f a == f a], there is only one solution. *)
+ aac_rewrite H.
+ reflexivity.
+ Qed.
+
+ End occurrence.
+
+ Section subst.
+ Variables a b c d : X.
+ Hypothesis H: forall x y, a*x*y*b == a*(x+y)*b.
+ Hypothesis H': forall x, x + x == x.
+
+ Goal a*c*d*c*d*b == a*c*d*b.
+ (** Here, there is only one possible context, but several substitutions; *)
+ aac_instances H.
+ (** we can select them with the proper keyword. *)
+ aac_rewrite H subst 1.
+ aac_rewrite H'.
+ aac_reflexivity.
+ Qed.
+ End subst.
+
+ (** As expected, one can use both keyword together to select the
+ correct subterm and the correct substitution. *)
+
+ Section both.
+ Variables a b c d : X.
+ Hypothesis H: forall x y, a*x*y*b == a*(x+y)*b.
+ Hypothesis H': forall x, x + x == x.
+
+ Goal a*c*d*c*d*b*b == a*(c*d+b)*b.
+ aac_instances H.
+ aac_rewrite H subterm 1 subst 1.
+ aac_rewrite H.
+ aac_rewrite H'.
+ aac_reflexivity.
+ Qed.
+
+ End both.
+
+ (** We now turn on explaining the distinction between [aac_rewrite]
+ and [aacu_rewrite]: [aac_rewrite] rejects solutions in which
+ variables are instantiated by units, the companion tactic,
+ [aacu_rewrite] allows such solutions. *)
+
+ Section dealing_with_units.
+ Variables a b c: X.
+ Hypothesis H: forall x, a*x*a == x.
+ Goal a*a == 1.
+ (** Here, x must be instantiated with 1, hence no solutions; *)
+ aac_instances H.
+ (** while we get solutions with the "aacu" tactic. *)
+ aacu_instances H.
+ aacu_rewrite H.
+ reflexivity.
+ Qed.
+
+ (** We introduced this distinction because it allows us to rule
+ out dummy cases in common situations: *)
+
+ Hypothesis H': forall x y z, x*y + x*z == x*(y+z).
+ Goal a*b*c + a*c+ a*b == a*(c+b*(1+c)).
+ (** 6 solutions without units, *)
+ aac_instances H'.
+ (** more than 52 with units. *)
+ aacu_instances H'.
+ Abort.
+
+ End dealing_with_units.
+End base.
+
+(** ** Declaring instances *)
+
+(** One can use one's own operations: it suffices to declare them as
+ instances of our classes. (Note that these instances are already
+ declared in file [Instances.v].) *)
+
+Section Peano.
+ Require Import Arith.
+
+ Program Instance nat_plus : Op_AC eq plus O.
+ Solve All Obligations using firstorder.
+
+ Program Instance nat_dot : Op_AC eq mult 1.
+ Solve All Obligations using firstorder.
+
+
+ (** Caveat: we need at least an instance of an operator that is AC
+ and one that is A for a given relation. However, one can reuse an
+ AC operator as an A operator. *)
+
+ Definition default_a := AC_A nat_dot. Existing Instance default_a.
+End Peano.
+
+(** ** Caveats *)
+
+Section Peano'.
+ Import Instances.Peano.
+
+ (** 1. We have a special treatment for units, thus, [S x + x] does not
+ match [S 0], which is considered as a unit (one) of the mult
+ operation. *)
+
+ Section caveat_one.
+ Definition double_plus_one x := 2*x + 1.
+ Hypothesis H : forall x, S x + x = double_plus_one x.
+ Goal S O = double_plus_one O.
+ try aac_rewrite H. (** 0 solutions (normal since it would use 0 to instantiate x) *)
+ try aacu_rewrite H. (** 0 solutions (abnormal) *)
+ Abort.
+ End caveat_one.
+
+ (** 2. We cannot at the moment have two classes with the same
+ units: in the following example, [0] is understood as the unit of
+ [max] rather than as the unit of [plus]. *)
+
+ Section max.
+ Program Instance aac_max : Op_AC eq Max.max O := Build_Op_AC _ _ _ Max.max_assoc Max.max_comm.
+ Variable a : nat.
+ Goal 0 + a = a.
+ try aac_reflexivity.
+ Abort.
+ End max.
+End Peano'.
+
+(** 3. If some computations are possible in the goal or in the
+hypothesis, the inference mechanism we use will make the
+conversion. However, it seems that in most cases, these conversions
+can be done by hand using simpl rather than rewrite. *)
+
+Section Z.
+
+ (* The order of the following lines is important in order to get the right scope afterwards. *)
+ Import ZArith.
+ Open Scope Z_scope.
+ Opaque Zmult.
+
+ Hypothesis dot_ann_left :forall x, x * 0 = 0.
+ Hypothesis dot_ann_right :forall x, 0 * x = 0.
+
+ Goal forall a, a*0 = 0.
+ intros. aacu_rewrite dot_ann_left. reflexivity.
+ Qed.
+
+ (** Here the tactic fails, since the 0*a is converted to 0, and no
+ rewrite can occur (even though Zmult is opaque). *)
+ Goal forall a, 0*a = 0.
+ intros. try aacu_rewrite dot_ann_left.
+ Abort.
+
+ (** Here the tactic fails, since the 0*x is converted to 0 in the hypothesis. *)
+ Goal forall a, a*0 = 0.
+ intros. try aacu_rewrite dot_ann_right.
+ Abort.
+
+
+End Z.
+
diff --git a/aac_rewrite.ml b/aac_rewrite.ml
new file mode 100644
index 0000000..c4c88e0
--- /dev/null
+++ b/aac_rewrite.ml
@@ -0,0 +1,308 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** aac_rewrite -- rewriting modulo *)
+
+(* functions to handle the failures of our tactic. Some should be
+ reported [anomaly], some are on behalf of the user [user_error]*)
+let anomaly msg =
+ Util.anomaly ("aac_tactics: " ^ msg)
+
+let user_error msg =
+ Util.error ("aac_tactics: " ^ msg)
+
+(* debug and timing functions *)
+let debug = false
+let printing = false
+let time = false
+
+let debug x =
+ if debug then
+ Printf.printf "%s\n%!" x
+
+let pr_constr msg constr =
+ if printing then
+ ( Pp.msgnl (Pp.str (Printf.sprintf "=====%s====" msg));
+ Pp.msgnl (Printer.pr_constr constr);
+ )
+
+let time msg tac =
+ if time then Coq.tclTIME msg tac
+ else tac
+
+let tac_or_exn tac exn msg =
+ fun gl -> try tac gl with e -> exn msg e
+
+(* helper to be used with the previous function: raise a new anomaly
+ except if a another one was previously raised *)
+let push_anomaly msg = function
+ | Util.Anomaly _ as e -> raise e
+ | _ -> anomaly msg
+
+module M = Matcher
+
+open Term
+open Names
+open Coqlib
+open Proof_type
+
+
+(** [find_applied_equivalence goal eq] try to ensure that the goal is
+ an applied equivalence relation, with two operands of the same type.
+
+ This function is transitionnal, as we plan to integrate with the
+ new rewrite features of Coq 8.3, that seems to handle this kind of
+ things.
+*)
+let find_applied_equivalence goal : Term.constr -> Coq.eqtype *Term.constr *Term.constr* Coq.goal_sigma = fun equation ->
+ let env = Tacmach.pf_env goal in
+ let evar_map = Tacmach.project goal in
+ match Coq.decomp_term equation with
+ | App(c,ca) when Array.length ca >= 2 ->
+ let n = Array.length ca in
+ let left = ca.(n-2) in
+ let right = ca.(n-1) in
+ let left_type = Tacmach.pf_type_of goal left in
+ (* let right_type = Tacmach.pf_type_of goal right in *)
+ let partial_app = mkApp (c, Array.sub ca 0 (n-2) ) in
+ let eq_type = Coq.Classes.mk_equivalence left_type partial_app in
+ begin
+ try
+ let evar_map, equivalence = Typeclasses.resolve_one_typeclass env evar_map eq_type in
+ let goal = Coq.goal_update goal evar_map in
+ ((left_type, partial_app), equivalence),left, right, goal
+ with
+ | Not_found -> user_error "The goal is not an applied equivalence"
+ end
+ | _ -> user_error "The goal is not an equation"
+
+
+(* [find_applied_equivalence_force] brutally decomposes the given
+ equation, and fail if we find a relation that is not the same as
+ the one we look for [r] *)
+let find_applied_equivalence_force rlt goal : Term.constr -> Term.constr *Term.constr* Coq.goal_sigma = fun equation ->
+ match Coq.decomp_term equation with
+ | App(c,ca) when Array.length ca >= 2 ->
+ let n = Array.length ca in
+ let left = ca.(n-2) in
+ let right = ca.(n-1) in
+ let partial_app = mkApp (c, Array.sub ca 0 (n-2) ) in
+ let _ = if snd rlt = partial_app then () else user_error "The goal and the hypothesis have different top-level relations" in
+
+ left, right, goal
+ | _ -> user_error "The hypothesis is not an equation"
+
+
+
+(** Build a couple of [t] from an hypothesis (variable names are not
+ relevant) *)
+let rec t_of_hyp goal rlt envs e = match Coq.decomp_term e with
+ | Prod (name,ty,c) -> let x, y = t_of_hyp goal rlt envs c
+ in x,y+1
+ | _ ->
+ let l,r ,goal = find_applied_equivalence_force rlt goal e in
+ let l,goal = Theory.Trans.t_of_constr goal rlt envs l in
+ let r,goal = Theory.Trans.t_of_constr goal rlt envs r in
+ (l,r),0
+
+(** @deprecated: [option_rip] will be removed as soon as a proper interface is
+ given to the user *)
+let option_rip = function | Some x -> x | None -> raise Not_found
+
+let mk_hyp_app conv c env =
+ let l = Matcher.Subst.to_list env in
+ let l = List.sort (fun (x,_) (y,_) -> compare y x) l in
+ let l = List.map (fun x -> conv (snd x)) l in
+ let v = Array.of_list l in
+ mkApp (c,v)
+
+
+
+(** {1 Tactics} *)
+
+(** [by_aac_reflexivity] is a sub-tactic that closes a sub-goal that
+ is merely a proof of equality of two terms modulo AAC *)
+
+let by_aac_reflexivity eqt envs (t : Matcher.Terms.t) (t' : Matcher.Terms.t) : Proof_type.tactic = fun goal ->
+ let maps, reifier,reif_letins = Theory.Trans.mk_reifier eqt envs goal in
+ let ((x,r),e) = eqt in
+
+ (* fold through a term and reify *)
+ let t = Theory.Trans.reif_constr_of_t reifier t in
+ let t' = Theory.Trans.reif_constr_of_t reifier t' in
+
+ (* Some letins *)
+ let eval, eval_letin = Coq.mk_letin' "eval_name" (mkApp (Lazy.force Theory.eval, [|x;r; maps.Theory.Trans.env_sym; maps.Theory.Trans.env_prd; maps.Theory.Trans.env_sum|])) in
+ let _ = pr_constr "left" t in
+ let _ = pr_constr "right" t' in
+ let t , t_letin = Coq.mk_letin "left" t in
+ let t', t_letin'= Coq.mk_letin "right" t' in
+ let apply_tac = Tactics.apply (
+ mkApp (Lazy.force Theory.decide_thm, [|x;r;e; maps.Theory.Trans.env_sym; maps.Theory.Trans.env_prd; maps.Theory.Trans.env_sum;t;t'|]))
+ in
+ Tactics.tclABSTRACT None
+ (Tacticals.tclTHENLIST
+ [
+ tac_or_exn reif_letins anomaly "invalid letin (1)";
+ tac_or_exn eval_letin anomaly "invalid letin (2)";
+ tac_or_exn t_letin anomaly "invalid letin (3)";
+ tac_or_exn t_letin' anomaly "invalid letin (4)";
+ tac_or_exn apply_tac anomaly "could not apply the decision theorem";
+ tac_or_exn (time "vm_norm" (Tactics.normalise_vm_in_concl)) anomaly "vm_compute failure";
+ Tacticals.tclORELSE Tactics.reflexivity
+ (Tacticals.tclFAIL 0 (Pp.str "Not an equality modulo A/AC"))
+ ])
+ goal
+
+
+(** The core tactic for aac_rewrite *)
+let core_aac_rewrite ?(l2r = true) envs eqt rew p subst left = fun goal ->
+ let rlt = fst eqt in
+ let t = Matcher.Subst.instantiate subst p in
+ let tr = Theory.Trans.raw_constr_of_t envs rlt t in
+ let tac_rew_l2r =
+ if l2r then Equality.rewriteLR rew else Equality.rewriteRL rew
+ in
+ pr_constr "transitivity through" tr;
+ Tacticals.tclTHENSV
+ (tac_or_exn (Tactics.transitivity tr) anomaly "Unable to make the transitivity step")
+ [|
+ (* si by_aac_reflexivity foire (pas un theoreme), il fait tclFAIL, et on rattrape ici *)
+ tac_or_exn (time "by_aac_refl" (by_aac_reflexivity eqt envs left t)) push_anomaly "Invalid transitivity step";
+ tac_or_exn (tac_rew_l2r) anomaly "Unable to rewrite";
+ |] goal
+
+exception NoSolutions
+
+(** Choose a substitution from a
+ [(int * Terms.t * Env.env Search.m) Search.m ] *)
+let choose_subst subterm sol m=
+ try
+ let (depth,pat,envm) = match subterm with
+ | None -> (* first solution *)
+ option_rip (Matcher.Search.choose m)
+ | Some x ->
+ List.nth (List.rev (Matcher.Search.to_list m)) x
+ in
+ let env = match sol with
+ None -> option_rip (Matcher.Search.choose envm)
+ | Some x -> List.nth (List.rev (Matcher.Search.to_list envm)) x
+ in
+ pat, env
+ with
+ | _ -> raise NoSolutions
+
+(** rewrite the constr modulo AC from left to right in the
+ left member of the goal
+*)
+let aac_rewrite rew ?(l2r=true) ?(show = false) ?strict ?occ_subterm ?occ_sol : Proof_type.tactic = fun goal ->
+ let envs = Theory.Trans.empty_envs () in
+ let rew_type = Tacmach.pf_type_of goal rew in
+ let (gl : Term.types) = Tacmach.pf_concl goal in
+ let _ = pr_constr "h" rew_type in
+ let _ = pr_constr "gl" gl in
+ let eqt, left, right, goal = find_applied_equivalence goal gl in
+ let rlt = fst eqt in
+ let left,goal = Theory.Trans.t_of_constr goal rlt envs left in
+ let right,goal = Theory.Trans.t_of_constr goal rlt envs right in
+ let (hleft,hright),hn = t_of_hyp goal rlt envs rew_type in
+ let pattern = if l2r then hleft else hright in
+ let m = Matcher.subterm ?strict pattern left in
+
+ let m = Matcher.Search.sort (fun (x,_,_) (y,_,_) -> x - y) m in
+ if show
+ then
+ let _ = Pp.msgnl (Pp.str "All solutions:") in
+ let _ = Pp.msgnl (Matcher.pp_all (fun t -> Printer.pr_constr (Theory.Trans.raw_constr_of_t envs rlt t) ) m) in
+ Tacticals.tclIDTAC goal
+ else
+ try
+ let pat,env = choose_subst occ_subterm occ_sol m in
+ let rew = mk_hyp_app (Theory.Trans.raw_constr_of_t envs rlt )rew env in
+ core_aac_rewrite ~l2r envs eqt rew pat env left goal
+ with
+ | NoSolutions -> Tacticals.tclFAIL 0
+ (Pp.str (if occ_subterm = None && occ_sol = None
+ then "No matching subterm found"
+ else "No such solution")) goal
+
+
+let aac_reflexivity : Proof_type.tactic = fun goal ->
+ let (gl : Term.types) = Tacmach.pf_concl goal in
+ let envs = Theory.Trans.empty_envs() in
+ let eqt, left, right, evar_map = find_applied_equivalence goal gl in
+ let rlt = fst eqt in
+ let left,goal = Theory.Trans.t_of_constr goal rlt envs left in
+ let right,goal = Theory.Trans.t_of_constr goal rlt envs right in
+ by_aac_reflexivity eqt envs left right goal
+
+
+open Tacmach
+open Tacticals
+open Tacexpr
+open Tacinterp
+open Extraargs
+
+ (* Adding entries to the grammar of tactics *)
+TACTIC EXTEND _aac1_
+| [ "aac_reflexivity" ] -> [ aac_reflexivity ]
+END
+
+
+TACTIC EXTEND _aac2_
+| [ "aac_rewrite" orient(o) constr(c) "subterm" integer(i) "subst" integer(j)] ->
+ [ fun gl -> aac_rewrite ~l2r:o ~strict:true ~occ_subterm:i ~occ_sol:j c gl ]
+END
+
+TACTIC EXTEND _aac3_
+| [ "aac_rewrite" orient(o) constr(c) "subst" integer(j)] ->
+ [ fun gl -> aac_rewrite ~l2r:o ~strict:true ~occ_subterm:0 ~occ_sol:j c gl ]
+END
+
+TACTIC EXTEND _aac4_
+| [ "aac_rewrite" orient(o) constr(c) "subterm" integer(i)] ->
+ [ fun gl -> aac_rewrite ~l2r:o ~strict:true ~occ_subterm:i ~occ_sol:0 c gl ]
+END
+
+TACTIC EXTEND _aac5_
+| [ "aac_rewrite" orient(o) constr(c) ] ->
+ [ fun gl -> aac_rewrite ~l2r:o ~strict:true c gl ]
+END
+
+TACTIC EXTEND _aac6_
+| [ "aac_instances" orient(o) constr(c)] ->
+ [ fun gl -> aac_rewrite ~l2r:o ~strict:true ~show:true c gl ]
+END
+
+
+
+
+TACTIC EXTEND _aacu2_
+| [ "aacu_rewrite" orient(o) constr(c) "subterm" integer(i) "subst" integer(j)] ->
+ [ fun gl -> aac_rewrite ~l2r:o ~occ_subterm:i ~occ_sol:j c gl ]
+END
+
+TACTIC EXTEND _aacu3_
+| [ "aacu_rewrite" orient(o) constr(c) "subst" integer(j)] ->
+ [ fun gl -> aac_rewrite ~l2r:o ~occ_subterm:0 ~occ_sol:j c gl ]
+END
+
+TACTIC EXTEND _aacu4_
+| [ "aacu_rewrite" orient(o) constr(c) "subterm" integer(i)] ->
+ [ fun gl -> aac_rewrite ~l2r:o ~occ_subterm:i ~occ_sol:0 c gl ]
+END
+
+TACTIC EXTEND _aacu5_
+| [ "aacu_rewrite" orient(o) constr(c) ] ->
+ [ fun gl -> aac_rewrite ~l2r:o c gl ]
+END
+
+TACTIC EXTEND _aacu6_
+| [ "aacu_instances" orient(o) constr(c)] ->
+ [ fun gl -> aac_rewrite ~l2r:o ~show:true c gl ]
+END
diff --git a/aac_rewrite.mli b/aac_rewrite.mli
new file mode 100644
index 0000000..81818b6
--- /dev/null
+++ b/aac_rewrite.mli
@@ -0,0 +1,59 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** Main module for the Coq plug-in ; provides the [aac_rewrite] and
+ [aac_reflexivity] tactics.
+
+ This file defines the entry point for the tactic aac_rewrite. It
+ does Joe-the-plumbing, given a goal, reifies the interesting
+ subterms (rewrited hypothesis, goal) into abstract syntax tree
+ {!Matcher.Terms} (see {!Theory.Trans.t_of_constr}). Then, we use the
+ results from the matcher to rebuild terms and make a transitivity
+ step toward a term in which the hypothesis can be rewritten using
+ the standard rewrite.
+
+ Doing so, we generate a sub-goal which we solve using a reflexive
+ decision procedure for the equality of terms modulo
+ AAC. Therefore, we also need to reflect the goal into a concrete
+ data-structure. See {i AAC.v} for more informations,
+ especially the data-type {b T} and the {b decide} theorem.
+
+*)
+
+(** {2 Transitional functions}
+
+ We define some plumbing functions that will be removed when we
+ integrate the new rewrite features of Coq 8.3
+*)
+
+(** [find_applied_equivalence goal eq] checks that the goal is
+ an applied equivalence relation, with two operands of the same
+ type.
+
+*)
+val find_applied_equivalence : Proof_type.goal Tacmach.sigma -> Term.constr -> Coq.eqtype * Term.constr * Term.constr * Proof_type.goal Tacmach.sigma
+
+(** Build a couple of [t] from an hypothesis (variable names are not
+ relevant) *)
+val t_of_hyp : Proof_type.goal Tacmach.sigma -> Coq.reltype -> Theory.Trans.envs -> Term.types -> (Matcher.Terms.t * Matcher.Terms.t) * int
+
+(** {2 Tactics} *)
+
+(** the [aac_reflexivity] tactic solves equalities modulo AAC, by
+ reflection: it reifies the goal to apply theorem [decide], from
+ file {i AAC.v}, and then concludes using [vm_compute]
+ and [reflexivity]
+*)
+val aac_reflexivity : Proof_type.tactic
+
+(** [aac_rewrite] is the tactic for in-depth reqwriting modulo AAC
+with some options to choose the orientation of the rewriting and a
+solution (first the subterm, then the solution)*)
+
+val aac_rewrite : Term.constr -> ?l2r:bool -> ?show:bool -> ?strict: bool -> ?occ_subterm:int -> ?occ_sol:int -> Proof_type.tactic
+
diff --git a/coq.ml b/coq.ml
new file mode 100644
index 0000000..731204c
--- /dev/null
+++ b/coq.ml
@@ -0,0 +1,212 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** Interface with Coq *)
+
+type constr = Term.constr
+
+open Term
+open Names
+open Coqlib
+
+
+(* The contrib name is used to locate errors when loading constrs *)
+let contrib_name = "aac_tactics"
+
+(* Getting constrs (primitive Coq terms) from exisiting Coq
+ libraries. *)
+let find_constant contrib dir s =
+ Libnames.constr_of_global (Coqlib.find_reference contrib dir s)
+
+let init_constant dir s = find_constant contrib_name dir s
+
+(* A clause specifying that the [let] should not try to fold anything
+ in the goal *)
+let nowhere =
+ { Tacexpr.onhyps = Some [];
+ Tacexpr.concl_occs = Rawterm.no_occurrences_expr
+ }
+
+
+let mk_letin (name: string) (constr: constr) : constr * Proof_type.tactic =
+ let name = (Names.id_of_string name) in
+ let letin =
+ (Tactics.letin_tac
+ None
+ (Name name)
+ constr
+ None (* or Some ty *)
+ nowhere
+ ) in
+ mkVar name, letin
+
+let mk_letin' (name: string) (constr: constr) : constr * Proof_type.tactic
+ =
+ constr, Tacticals.tclIDTAC
+
+
+
+(** {2 General functions} *)
+
+type goal_sigma = Proof_type.goal Tacmach.sigma
+let goal_update (goal : goal_sigma) evar_map : goal_sigma=
+ let it = Tacmach.sig_it goal in
+ Tacmach.re_sig it evar_map
+
+let resolve_one_typeclass goal ty : constr*goal_sigma=
+ let env = Tacmach.pf_env goal in
+ let evar_map = Tacmach.project goal in
+ let em,c = Typeclasses.resolve_one_typeclass env evar_map ty in
+ c, (goal_update goal em)
+
+let nf_evar goal c : Term.constr=
+ let evar_map = Tacmach.project goal in
+ Evarutil.nf_evar evar_map c
+
+let evar_unit (gl : goal_sigma) (rlt : constr * constr) : constr * goal_sigma =
+ let env = Tacmach.pf_env gl in
+ let evar_map = Tacmach.project gl in
+ let (em,x) = Evarutil.new_evar evar_map env (fst rlt) in
+ x,(goal_update gl em)
+
+let evar_binary (gl: goal_sigma) (rlt: constr * constr) =
+ let env = Tacmach.pf_env gl in
+ let evar_map = Tacmach.project gl in
+ let x = fst rlt in
+ let ty = mkArrow x (mkArrow x x) in
+ let (em,x) = Evarutil.new_evar evar_map env ty in
+ x,( goal_update gl em)
+
+(* decomp_term : constr -> (constr, types) kind_of_term *)
+let decomp_term c = kind_of_term (strip_outer_cast c)
+
+
+(** {2 Bindings with Coq' Standard Library} *)
+
+module Pair = struct
+
+ let path = ["Coq"; "Init"; "Datatypes"]
+ let typ = lazy (init_constant path "prod")
+ let pair = lazy (init_constant path "pair")
+end
+
+module Pos = struct
+
+ let path = ["Coq" ; "NArith"; "BinPos"]
+ let typ = lazy (init_constant path "positive")
+ let xI = lazy (init_constant path "xI")
+ let xO = lazy (init_constant path "xO")
+ let xH = lazy (init_constant path "xH")
+
+ (* A coq positive from an int *)
+ let of_int n =
+ let rec aux n =
+ begin match n with
+ | n when n < 1 -> assert false
+ | 1 -> Lazy.force xH
+ | n -> mkApp
+ (
+ (if n mod 2 = 0
+ then Lazy.force xO
+ else Lazy.force xI
+ ), [| aux (n/2)|]
+ )
+ end
+ in
+ aux n
+end
+
+module Nat = struct
+ let path = ["Coq" ; "Init"; "Datatypes"]
+ let typ = lazy (init_constant path "nat")
+ let _S = lazy (init_constant path "S")
+ let _O = lazy (init_constant path "O")
+ (* A coq nat from an int *)
+ let of_int n =
+ let rec aux n =
+ begin match n with
+ | n when n < 0 -> assert false
+ | 0 -> Lazy.force _O
+ | n -> mkApp
+ (
+ (Lazy.force _S
+ ), [| aux (n-1)|]
+ )
+ end
+ in
+ aux n
+end
+
+(** Lists from the standard library*)
+module List = struct
+ let path = ["Coq"; "Lists"; "List"]
+ let typ = lazy (init_constant path "list")
+ let nil = lazy (init_constant path "nil")
+ let cons = lazy (init_constant path "cons")
+ let cons ty h t =
+ mkApp (Lazy.force cons, [| ty; h ; t |])
+ let nil ty =
+ (mkApp (Lazy.force nil, [| ty |]))
+ let rec of_list ty = function
+ | [] -> nil ty
+ | t::q -> cons ty t (of_list ty q)
+ let type_of_list ty =
+ mkApp (Lazy.force typ, [|ty|])
+end
+
+(** Morphisms *)
+module Classes =
+struct
+ let classes_path = ["Coq";"Classes"; ]
+ let morphism = lazy (init_constant (classes_path@["Morphisms"]) "Proper")
+
+ (** build the constr [Proper rel t] *)
+ let mk_morphism ty rel t =
+ mkApp (Lazy.force morphism, [| ty; rel; t|])
+
+ let equivalence = lazy (init_constant (classes_path@ ["RelationClasses"]) "Equivalence")
+
+ (** build the constr [Equivalence ty rel] *)
+ let mk_equivalence ty rel =
+ mkApp (Lazy.force equivalence, [| ty; rel|])
+end
+
+(** a [mset] is a ('a * pos) list *)
+let mk_mset ty (l : (constr * int) list) =
+ let pos = Lazy.force Pos.typ in
+ let pair_ty = mkApp (Lazy.force Pair.typ , [| ty; pos|]) in
+ let pair (x : constr) (ar : int) =
+ mkApp (Lazy.force Pair.pair, [| ty ; pos ; x ; Pos.of_int ar|]) in
+ let rec aux = function
+ | [] -> List.nil pair_ty
+ | (t,ar)::q -> List.cons pair_ty (pair t ar) (aux q)
+ in
+ aux l
+
+(** x r *)
+type reltype = Term.constr * Term.constr
+(** x r e *)
+type eqtype = reltype * Term.constr
+
+
+(** {1 Tacticals} *)
+
+let tclTIME msg tac = fun gl ->
+ let u = Sys.time () in
+ let r = tac gl in
+ let _ = Pp.msgnl (Pp.str (Printf.sprintf "%s:%fs" msg (Sys.time ()-. u))) in
+ r
+
+let tclDEBUG msg tac = fun gl ->
+ let _ = Pp.msgnl (Pp.str msg) in
+ tac gl
+
+let tclPRINT tac = fun gl ->
+ let _ = Pp.msgnl (Printer.pr_constr (Tacmach.pf_concl gl)) in
+ tac gl
+
diff --git a/coq.mli b/coq.mli
new file mode 100644
index 0000000..588a922
--- /dev/null
+++ b/coq.mli
@@ -0,0 +1,117 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** Interface with Coq where we import several definitions from Coq's
+ standard library.
+
+ This general purpose library could be reused by other plugins.
+
+ Some salient points:
+ - we use Caml's module system to mimic Coq's one, and avoid cluttering
+ the namespace;
+ - we also provide several handlers for standard coq tactics, in
+ order to provide a unified setting (we replace functions that
+ modify the evar_map by functions that operate on the whole
+ goal, and repack the modified evar_map with it).
+
+*)
+
+(** {2 Getting Coq terms from the environment} *)
+
+val init_constant : string list -> string -> Term.constr
+
+(** {2 General purpose functions} *)
+
+type goal_sigma = Proof_type.goal Tacmach.sigma
+val goal_update : goal_sigma -> Evd.evar_map -> goal_sigma
+val resolve_one_typeclass : Proof_type.goal Tacmach.sigma -> Term.types -> Term.constr * goal_sigma
+val nf_evar : goal_sigma -> Term.constr -> Term.constr
+val evar_unit :goal_sigma ->Term.constr*Term.constr-> Term.constr* goal_sigma
+val evar_binary: goal_sigma -> Term.constr*Term.constr -> Term.constr* goal_sigma
+
+
+(** [mk_letin name v] binds the constr [v] using a letin tactic *)
+val mk_letin : string ->Term.constr ->Term.constr * Proof_type.tactic
+
+(** [mk_letin' name v] is a drop-in replacement for [mk_letin' name v]
+ that does not make a binding (useful to test whether using lets is
+ efficient) *)
+val mk_letin' : string ->Term.constr ->Term.constr * Proof_type.tactic
+
+(* decomp_term : constr -> (constr, types) kind_of_term *)
+val decomp_term : Term.constr -> (Term.constr , Term.types) Term.kind_of_term
+
+
+(** {2 Bindings with Coq' Standard Library} *)
+
+(** Coq lists *)
+module List:
+sig
+ (** [of_list ty l] *)
+ val of_list:Term.constr ->Term.constr list ->Term.constr
+
+ (** [type_of_list ty] *)
+ val type_of_list:Term.constr ->Term.constr
+end
+
+(** Coq pairs *)
+module Pair:
+sig
+ val typ:Term.constr lazy_t
+ val pair:Term.constr lazy_t
+end
+
+(** Coq positive numbers (pos) *)
+module Pos:
+sig
+ val typ:Term.constr lazy_t
+ val of_int: int ->Term.constr
+end
+
+(** Coq unary numbers (peano) *)
+module Nat:
+sig
+ val typ:Term.constr lazy_t
+ val of_int: int ->Term.constr
+end
+
+
+(** Coq typeclasses *)
+module Classes:
+sig
+ val mk_morphism: Term.constr -> Term.constr -> Term.constr -> Term.constr
+ val mk_equivalence: Term.constr -> Term.constr -> Term.constr
+end
+
+(** Both in OCaml and Coq, we represent finite multisets using
+ weighted lists ([('a*int) list]), see {!Matcher.mset}.
+
+ [mk_mset ty l] constructs a Coq multiset from an OCaml multiset
+ [l] of Coq terms of type [ty] *)
+
+val mk_mset:Term.constr -> (Term.constr * int) list ->Term.constr
+
+(** pairs [(x,r)] such that [r: Relation x] *)
+type reltype = Term.constr * Term.constr
+
+(** triples [((x,r),e)] such that [e : @Equivalence x r]*)
+type eqtype = reltype * Term.constr
+
+
+(** {2 Some tacticials} *)
+
+(** time the execution of a tactic *)
+val tclTIME : string -> Proof_type.tactic -> Proof_type.tactic
+
+(** emit debug messages to see which tactics are failing *)
+val tclDEBUG : string -> Proof_type.tactic -> Proof_type.tactic
+
+(** print the current goal *)
+val tclPRINT : Proof_type.tactic -> Proof_type.tactic
+
+
diff --git a/files.txt b/files.txt
new file mode 100644
index 0000000..7fa1801
--- /dev/null
+++ b/files.txt
@@ -0,0 +1,7 @@
+matcher.ml
+coq.ml
+theory.ml
+aac_rewrite.ml
+AAC.v
+Instances.v
+Tutorial.v
diff --git a/magic.txt b/magic.txt
new file mode 100644
index 0000000..d0272c3
--- /dev/null
+++ b/magic.txt
@@ -0,0 +1,4 @@
+-custom "mkdir -p doc; $(CAMLBIN)ocamldoc -html -rectypes -d doc -m A $(ZDEBUG) $(ZFLAGS) $^ && touch doc" "$(MLIFILES)" doc
+-custom "$(CAMLOPTLINK) $(ZDEBUG) $(ZFLAGS) -shared -linkall -o aac_tactics.cmxs $(CMXFILES)" "$(CMXFILES)" aac_tactics.cmxs
+-custom "$(CAMLLINK) -g -a -o aac_tactics.cma $(CMOFILES)" "$(CMOFILES)" aac_tactics.cma
+-custom "$(CAMLBIN)ocamldep -slash $(OCAMLLIBS) $^ > .depend" "$(MLIFILES)" .depend
diff --git a/make_makefile b/make_makefile
new file mode 100755
index 0000000..3cfc096
--- /dev/null
+++ b/make_makefile
@@ -0,0 +1,49 @@
+# - set variable MLIFILES so that it can be used in custom targets to
+# build documentation and dependencies for .mli files
+# - remove useless ' -I . .../plugins/ ' lines so that we can read something in the compilation log
+# - rename the rule for Makefile auto-regeneration, and replace it with an appropriate command
+# - patch the rule for cleaning doc
+# - include the .depend custom target, to take .mli dependencies into account
+
+(
+coq_makefile -no-install -opt MLIFILES = '$(MLFILES:.ml=.mli)' $(cat files.txt) -f magic.txt \
+ | sed 's|.*/plugins/.*||g;s|Makefile:|\.dummy:|g;s|rm -f doc|rm -rf doc|g'
+cat <<EOF
+
+# sanity checks
+tests: Tests.vo
+
+# override inadequate coq_makefile auto-regeneration
+Makefile: make_makefile magic.txt files.txt
+ . make_makefile
+
+# We want to keep the proofs only in the Tutorial
+tutorial: Tutorial.vo Tutorial.glob
+ \$(COQDOC) --no-index --no-externals -html \$(COQDOCLIBS) -d html Tutorial.v
+
+world: \$(VOFILES) doc gallinahtml tutorial
+
+# additional dependencies for AAC (since aac_tactics.cmxs/cma are not
+# around the first time we run make, coqdep issues a warning and
+# ignores this dependency)
+# (one can safely select one of theses dependencies according to
+# coq' compilation mode--bytecode or native)
+AAC.vo: aac_tactics.cmxs aac_tactics.cma
+
+
+# .depend contains dependencies for .mli files
+-include .depend
+.SECONDARY: .depend
+
+EOF
+) > Makefile
+
+
+
+# TOTHINK: coq_makefile est bête, [make all] compile systématiquement
+# les .cmxs associés à chaque module, alors que ça ne sert à rien,
+# seul aac_rewrite.cmxs compte
+
+# problemes lorsqu'un module porte un meme nom qu'un module predefini
+# (matching par exemple), prendre garde si ce probleme s'etend a la
+# confusion module/nom de librairie
diff --git a/matcher.ml b/matcher.ml
new file mode 100644
index 0000000..176b660
--- /dev/null
+++ b/matcher.ml
@@ -0,0 +1,980 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** This module defines our matching functions, modulo associativity
+ and commutativity (AAC).
+
+ The basic idea is to find a substitution [env] such that the
+ pattern [p] instantiated by [env] is equal to [t] modulo AAC.
+
+ We proceed by structural decomposition of the pattern, and try all
+ possible non-deterministic split of the subject when needed. The
+ function {!matcher} is limited to top-level matching, that is, the
+ subject must make a perfect match against the pattern ([x+x] do
+ not match [a+a+b] ). We use a search monad {!Search} to perform
+ non-deterministic splits in an almost transparent way. We also
+ provide a function {!subterm} for finding a match that is a
+ subterm modulo AAC of the subject. Therefore, we are able to solve
+ the aforementioned case [x+x] against [a+b+a].
+
+ This file is structured as follows. First, we define the search
+ monad. Then,we define the two representations of terms (one
+ representing the AST, and one in normal form ), and environments
+ from variables to terms. Then, we use these parts to solve
+ matching problem. Finally, we wrap this function {!matcher} into
+ {!subterm}
+*)
+
+
+let debug = false
+let time = false
+
+
+let time f x fmt =
+ if time then
+ let t = Sys.time() in
+ let r = f x in
+ Printf.printf fmt (Sys.time () -. t);
+ r
+ else f x
+
+
+
+type symbol = int
+type var = int
+
+
+(****************)
+(* Search Monad *)
+(****************)
+
+
+(** The {!Search} module contains a search monad that allows to
+ express, in a legible maner, programs that solves combinatorial
+ problems
+
+ @see <http://spivey.oriel.ox.ac.uk/mike/search-jfp.pdf> the
+ inspiration of this module
+*)
+module Search : sig
+ (** A data type that represent a collection of ['a] *)
+ type 'a m
+ (** bind and return *)
+ val ( >> ) : 'a m -> ('a -> 'b m) -> 'b m
+ val return : 'a -> 'a m
+ (** non-deterministic choice *)
+ val ( >>| ) : 'a m -> 'a m -> 'a m
+ (** failure *)
+ val fail : unit -> 'a m
+ (** folding through the collection *)
+ val fold : ('a -> 'b -> 'b) -> 'a m -> 'b -> 'b
+ (** derived facilities *)
+ val sprint : ('a -> string) -> 'a m -> string
+ val count : 'a m -> int
+ val choose : 'a m -> 'a option
+ val to_list : 'a m -> 'a list
+ val sort : ('a -> 'a -> int) -> 'a m -> 'a m
+ val is_empty: 'a m -> bool
+end
+= struct
+
+ type 'a m = | F of 'a
+ | N of 'a m list
+
+ let fold (f : 'a -> 'b -> 'b) (m : 'a m) (acc : 'b) =
+ let rec aux acc = function
+ F x -> f x acc
+ | N l ->
+ (List.fold_left (fun acc x ->
+ match x with
+ | (N []) -> acc
+ | x -> aux acc x
+ ) acc l)
+ in
+ aux acc m
+
+
+
+ let rec (>>) : 'a m -> ('a -> 'b m) -> 'b m =
+ fun m f ->
+ match m with
+ | F x -> f x
+ | N l ->
+ N (List.fold_left (fun acc x ->
+ match x with
+ | (N []) -> acc
+ | x -> (x >> f)::acc
+ ) [] l)
+
+ let (>>|) (m : 'a m) (n :'a m) : 'a m = match (m,n) with
+ | N [],_ -> n
+ | _,N [] -> m
+ | F x, N l -> N (F x::l)
+ | N l, F x -> N (F x::l)
+ | x,y -> N [x;y]
+
+ let return : 'a -> 'a m = fun x -> F x
+ let fail : unit -> 'a m = fun () -> N []
+
+ let sprint f m =
+ fold (fun x acc -> Printf.sprintf "%s\n%s" acc (f x)) m ""
+ let rec count = function
+ | F _ -> 1
+ | N l -> List.fold_left (fun acc x -> acc+count x) 0 l
+
+ let opt_comb f x y = match x with None -> f y | _ -> x
+
+ let rec choose = function
+ | F x -> Some x
+ | N l -> List.fold_left (fun acc x ->
+ opt_comb choose acc x
+ ) None l
+
+ let is_empty = fun x -> choose x = None
+
+ let to_list m = (fold (fun x acc -> x::acc) m [])
+
+ let sort f m =
+ N (List.map (fun x -> F x) (List.sort f (to_list m)))
+end
+
+open Search
+
+
+type 'a mset = ('a * int) list
+let linear p =
+ let rec ncons t l = function
+ | 0 -> l
+ | n -> t::ncons t l (n-1)
+ in
+ let rec aux = function
+ [ ] -> []
+ | (t,n)::q -> let q = aux q in
+ ncons t q n
+ in aux p
+
+
+
+(** The module {!Terms} defines two different types for expressions.
+
+ - a public type {!Terms.t} that represent abstract syntax trees of
+ expressions with binary associative (and commutative) operators
+
+ - a private type {!Terms.nf_term} that represent an equivalence
+ class for terms that are equal modulo AAC. The constructions
+ functions on this type ensure the property that the term is in
+ normal form (that is, no sum can appear as a subterm of the same
+ sum, no trailing units, etc...).
+
+*)
+
+module Terms : sig
+
+ (** {1 Abstract syntax tree of terms}
+
+ Terms represented using this datatype are representation of the
+ AST of an expression. *)
+
+ type t =
+ Dot of (symbol * t * t)
+ | One of symbol
+ | Plus of (symbol * t * t)
+ | Zero of symbol
+ | Sym of (symbol * t array)
+ | Var of var
+
+ val equal_aac : t -> t -> bool
+ val size: t -> int
+ (** {1 Terms in normal form}
+
+ A term in normal form is the canonical representative of the
+ equivalence class of all the terms that are equal modulo
+ Associativity and Commutativity. Outside the {!Matcher} module,
+ one does not need to access the actual representation of this
+ type. *)
+
+ type nf_term = private
+ | TAC of symbol * nf_term mset
+ | TA of symbol * nf_term list
+ | TSym of symbol *nf_term list
+ | TVar of var
+
+
+ (** {2 Constructors: we ensure that the terms are always
+ normalised} *)
+ val mk_TAC : symbol -> nf_term mset -> nf_term
+ val mk_TA : symbol -> nf_term list -> nf_term
+ val mk_TSym : symbol -> nf_term list -> nf_term
+ val mk_TVar : var -> nf_term
+
+ (** {2 Comparisons} *)
+
+ val nf_term_compare : nf_term -> nf_term -> int
+ val nf_equal : nf_term -> nf_term -> bool
+
+ (** {2 Printing function} *)
+ val sprint_nf_term : nf_term -> string
+
+ (** {2 Conversion functions} *)
+ val term_of_t : t -> nf_term
+ val t_of_term : nf_term -> t
+end
+ = struct
+
+ type t =
+ Dot of (symbol * t * t)
+ | One of symbol
+ | Plus of (symbol * t * t)
+ | Zero of symbol
+ | Sym of (symbol * t array)
+ | Var of var
+
+ let rec size = function
+ | Dot (_,x,y)
+ | Plus (_,x,y) -> size x+ size y + 1
+ | Sym (_,v)-> Array.fold_left (fun acc x -> size x + acc) 1 v
+ | _ -> 1
+
+
+ type nf_term =
+ | TAC of symbol * nf_term mset
+ | TA of symbol * nf_term list
+ | TSym of symbol *nf_term list
+ | TVar of var
+
+
+
+ (** {2 Comparison} *)
+
+ let nf_term_compare = Pervasives.compare
+ let nf_equal a b = a = b
+
+ (** {2 Constructors: we ensure that the terms are always
+ normalised} *)
+
+ (** {3 Pre constructors : These constructors ensure that sums and
+ products are not degenerated (no trailing units)} *)
+ let mk_TAC' s l = match l with
+ | [t,0] -> TAC (s,[])
+ | [t,1] -> t
+ | _ -> TAC (s,l)
+ let mk_TA' s l = match l with [t] -> t
+ | _ -> TA (s,l)
+
+
+
+ (** [merge_ac comp l1 l2] merges two lists of terms with coefficients
+ into one. Terms that are equal modulo the comparison function
+ [comp] will see their arities added. *)
+ let merge_ac (compare : 'a -> 'a -> int) (l : 'a mset) (l' : 'a mset) : 'a mset =
+ let rec aux l l'=
+ match l,l' with
+ | [], _ -> l'
+ | _, [] -> l
+ | (t,tar)::q, (t',tar')::q' ->
+ begin match compare t t' with
+ | 0 -> ( t,tar+tar'):: aux q q'
+ | -1 -> (t, tar):: aux q l'
+ | _ -> (t', tar'):: aux l q'
+ end
+ in aux l l'
+
+ (** [merge_map f l] uses the combinator [f] to combine the head of the
+ list [l] with the merge_maped tail of [l] *)
+ let rec merge_map (f : 'a -> 'b list -> 'b list) (l : 'a list) : 'b list =
+ match l with
+ | [] -> []
+ | t::q -> f t (merge_map f q)
+
+
+ (** This function has to deal with the arities *)
+ let rec merge l l' =
+ merge_ac nf_term_compare l l'
+
+ let extract_A s t =
+ match t with
+ | TA (s',l) when s' = s -> l
+ | _ -> [t]
+
+ let extract_AC s (t,ar) : nf_term mset =
+ match t with
+ | TAC (s',l) when s' = s -> List.map (fun (x,y) -> (x,y*ar)) l
+ | _ -> [t,ar]
+
+
+ (** {3 Constructors of {!nf_term}}*)
+
+ let mk_TAC s (l : (nf_term *int) list) =
+ mk_TAC' s
+ (merge_map (fun u l -> merge (extract_AC s u) l) l)
+ let mk_TA s l =
+ mk_TA' s
+ (merge_map (fun u l -> (extract_A s u) @ l) l)
+ let mk_TSym s l = TSym (s,l)
+ let mk_TVar v = TVar v
+
+
+ (** {2 Printing function} *)
+ let print_binary_list single unit binary l =
+ let rec aux l =
+ match l with
+ [] -> unit
+ | [t] -> single t
+ | t::q ->
+ let r = aux q in
+ Printf.sprintf "%s" (binary (single t) r)
+ in
+ aux l
+
+ let sprint_ac single (l : 'a mset) =
+ (print_binary_list
+ (fun (x,t) ->
+ if t = 1
+ then single x
+ else Printf.sprintf "%i*%s" t (single x)
+ )
+ "0"
+ (fun x y -> x ^ " , " ^ y)
+ l
+ )
+
+ let print_symbol single s l =
+ match l with
+ [] -> Printf.sprintf "%i" s
+ | _ ->
+ Printf.sprintf "%i(%s)"
+ s
+ (print_binary_list single "" (fun x y -> x ^ "," ^ y) l)
+
+
+ let print_ac_list single s l =
+ Printf.sprintf "[%i:AC]{%s}"
+ s
+ (print_binary_list
+ single
+ "0"
+ (fun x y -> x ^ " , " ^ y)
+ l
+ )
+
+
+ let print_a single s l =
+ Printf.sprintf "[%i:A]{%s}"
+ s
+ (print_binary_list single "1" (fun x y -> x ^ " , " ^ y) l)
+
+ let rec sprint_nf_term = function
+ | TSym (s,l) -> print_symbol sprint_nf_term s l
+ | TAC (s,l) ->
+ Printf.sprintf "[%i:AC]{%s}" s
+ (sprint_ac
+ sprint_nf_term
+ l)
+ | TA (s,l) -> print_a sprint_nf_term s l
+ | TVar v -> Printf.sprintf "x%i" v
+
+
+
+ (** {2 Conversion functions} *)
+
+ (* rebuilds a tree out of a list *)
+ let rec binary_of_list f comb null l =
+ let l = List.rev l in
+ let rec aux = function
+ | [] -> null
+ | [t] -> f t
+ | t::q -> comb (aux q) (f t)
+ in
+ aux l
+
+ let rec term_of_t : t -> nf_term = function
+ | Dot (s,l,r) ->
+ let l = term_of_t l in
+ let r = term_of_t r in
+ mk_TA s [l;r]
+ | Plus (s,l,r) ->
+ let l = term_of_t l in
+ let r = term_of_t r in
+ mk_TAC ( s) [l,1;r,1]
+ | One x ->
+ mk_TA ( x) []
+ | Zero x ->
+ mk_TAC ( x) []
+ | Sym (s,t) ->
+ let t = Array.to_list t in
+ let t = List.map term_of_t t in
+ mk_TSym ( s) t
+ | Var i ->
+ mk_TVar ( i)
+
+ let rec t_of_term : nf_term -> t = function
+ | TAC (s,l) ->
+ (binary_of_list
+ t_of_term
+ (fun l r -> Plus ( s,l,r))
+ (Zero ( s))
+ (linear l)
+ )
+ | TA (s,l) ->
+ (binary_of_list
+ t_of_term
+ (fun l r -> Dot ( s,l,r))
+ (One ( s))
+ l
+ )
+ | TSym (s,l) ->
+ let v = Array.of_list l in
+ let v = Array.map (t_of_term) v in
+ Sym ( s,v)
+ | TVar x -> Var x
+
+
+ let equal_aac x y =
+ nf_equal (term_of_t x) (term_of_t y)
+ end
+
+(** Terms environments defined as association lists from variables to
+ terms in normal form {! Terms.nf_term} *)
+module Subst : sig
+ type t
+
+ val find : t -> var -> Terms.nf_term option
+ val add : t -> var -> Terms.nf_term -> t
+ val empty : t
+ val instantiate : t -> Terms.t -> Terms.t
+ val sprint : t -> string
+ val to_list : t -> (var*Terms.t) list
+end
+ =
+struct
+ open Terms
+
+ (** Terms environments, with nf_terms, to avoid costly conversions
+ of {!Terms.nf_terms} to {!Terms.t}, that will be mostly discarded*)
+ type t = (var * nf_term) list
+
+ let find : t -> var -> nf_term option = fun t x ->
+ try Some (List.assoc x t) with | _ -> None
+ let add t x v = (x,v) :: t
+ let empty = []
+
+ let sprint (l : t) =
+ match l with
+ | [] -> Printf.sprintf "Empty environment\n"
+ | _ ->
+
+ let s = List.fold_left
+ (fun acc (x,y) ->
+ Printf.sprintf "%sX%i -> %s\n"
+ acc
+ x
+ (sprint_nf_term y)
+ )
+ ""
+ (List.rev l) in
+ Printf.sprintf "%s\n%!" s
+
+
+
+ (** [instantiate] is an homomorphism except for the variables*)
+ let instantiate (t: t) (x:Terms.t) : Terms.t =
+ let rec aux = function
+ | One _ as x -> x
+ | Zero _ as x -> x
+ | Sym (s,t) -> Sym (s,Array.map aux t)
+ | Plus (s,l,r) -> Plus (s, aux l, aux r)
+ | Dot (s,l,r) -> Dot (s, aux l, aux r)
+ | Var i ->
+ begin match find t i with
+ | None -> Util.error "aac_tactics: instantiate failure"
+ | Some x -> t_of_term x
+ end
+ in aux x
+
+ let to_list t = List.map (fun (x,y) -> x,Terms.t_of_term y) t
+end
+
+(******************)
+(* MATCHING UTILS *)
+(******************)
+
+open Terms
+
+(** First, we need to be able to perform non-deterministic choice of
+ term splitting to satisfy a pattern. Indeed, we want to show that:
+ (x+a*b)*c <= a*b*c
+*)
+let a_nondet_split t : ('a list * 'a list) m =
+ let rec aux l l' =
+ match l' with
+ | [] ->
+ return ( l,[])
+ | t::q ->
+ return ( l,l' )
+ >>| aux (l @ [t]) q
+ in
+ aux [] t
+
+(** Same as the previous [a_nondet_split], but split the list in 3
+ parts *)
+let a_nondet_middle t : ('a list * 'a list * 'a list) m =
+ a_nondet_split t >>
+ (fun left, right ->
+ a_nondet_split left >>
+ (fun left, middle -> return (left, middle, right))
+ )
+
+(** Non deterministic splits of ac lists *)
+let dispatch f n =
+ let rec aux k =
+ if k = 0 then return (f n 0)
+ else return (f (n-k) k) >>| aux (k-1)
+ in
+ aux (n )
+
+let add_with_arith x ar l =
+ if ar = 0 then l else (x,ar) ::l
+
+let ac_nondet_split (l : 'a mset) : ('a mset * 'a mset) m =
+ let rec aux = function
+ | [] -> return ([],[])
+ | (t,tar)::q ->
+ aux q
+ >>
+ (fun (left,right) ->
+ dispatch (fun arl arr ->
+ add_with_arith t arl left,
+ add_with_arith t arr right
+ )
+ tar
+ )
+ in
+ aux l
+
+(** Try to affect the variable [x] to each left factor of [t]*)
+let var_a_nondet_split ?(strict=false) env current x t =
+ a_nondet_split t
+ >>
+ (fun (l,r) ->
+ if strict && l=[] then fail() else
+ return ((Subst.add env x (mk_TA current l)), r)
+ )
+
+(** Try to affect variable [x] to _each_ subset of t. *)
+let var_ac_nondet_split ?(strict=false) (s : symbol) env (x : var) (t : nf_term mset) : (Subst.t * (nf_term mset)) m =
+ ac_nondet_split t
+ >>
+ (fun (subset,compl) ->
+ if strict && subset=[] then fail() else
+ return ((Subst.add env x (mk_TAC s subset)), compl)
+ )
+
+(** See the term t as a given AC symbol. Unwrap the first constructor
+ if necessary *)
+let get_AC (s : symbol) (t : nf_term) : (nf_term *int) list =
+ match t with
+ | TAC (s',l) when s' = s -> l
+ | _ -> [t,1]
+
+(** See the term t as a given A symbol. Unwrap the first constructor
+ if necessary *)
+let get_A (s : symbol) (t : nf_term) : nf_term list =
+ match t with
+ | TA (s',l) when s' = s -> l
+ | _ -> [t]
+
+(** See the term [t] as an symbol [s]. Fail if it is not such
+ symbol. *)
+let get_Sym s t =
+ match t with
+ | TSym (s',l) when s' = s -> return l
+ | _ -> fail ()
+
+(*************)
+(* A Removal *)
+(*************)
+
+(** We remove the left factor v in a term list. This function runs
+ linearly with respect to the size of the first pattern symbol *)
+
+let left_factor current (v : nf_term) (t : nf_term list) =
+ let rec aux a b =
+ match a,b with
+ | t::q , t' :: q' when nf_equal t t' -> aux q q'
+ | [], q -> return q
+ | _, _ -> fail ()
+ in
+ match v with
+ | TA (s,l) when s = current -> aux l t
+ | _ ->
+ begin match t with
+ | [] -> fail ()
+ | t::q ->
+ if nf_equal v t
+ then return q
+ else fail ()
+ end
+
+
+(**************)
+(* AC Removal *)
+(**************)
+
+(** [fold_acc] gather all elements of a list that satisfies a
+ predicate, and combine them with the residual of the list. That
+ is, each element of the residual contains exactly one element less
+ than the original term.
+
+ TODO : This function not as efficient as it could be
+*)
+
+let pick_sym (s : symbol) (t : nf_term mset ) =
+ let rec aux front back =
+ match back with
+ | [] -> fail ()
+ | (t,tar)::q ->
+ begin match t with
+ | TSym (s',v') when s = s' ->
+ let back =
+ if tar > 1
+ then (t,tar -1) ::q
+ else q
+ in
+ return (v' , List.rev_append front back )
+ >>| aux ((t,tar)::front) q
+ | _ -> aux ((t,tar)::front) q
+ end
+ in
+ aux [] t
+
+
+
+(** We have to check if we are trying to remove a unit from a term*)
+let is_unit_AC s t =
+ nf_equal t (mk_TAC s [])
+
+let is_same_AC s t : nf_term mset option=
+ match t with
+ TAC (s',l) when s = s' -> Some l
+ | _ -> None
+
+(** We want to remove the term [v] from the term list [t] under an AC
+ symbol *)
+let single_AC_factor (s : symbol) (v : nf_term) v_ar (t : nf_term mset) : (nf_term mset) m =
+ let rec aux front back =
+ match back with
+ | [] -> fail ()
+ | (t,tar)::q ->
+ begin
+ if nf_equal v t
+ then
+ match () with
+ | _ when tar < v_ar -> fail ()
+ | _ when tar = v_ar -> return (List.rev_append front q)
+ | _ -> return (List.rev_append front ((t,tar-v_ar)::q))
+ else
+ aux ((t,tar) :: front) q
+ end
+ in
+ if is_unit_AC s v
+ then
+ return t
+ else
+ aux [] t
+
+let factor_AC (s : symbol) (v: nf_term) (t : nf_term mset) : ( nf_term mset ) m =
+ match is_same_AC s v with
+ | None -> single_AC_factor s v 1 t
+ | Some l ->
+ (* We are trying to remove an AC factor *)
+ List.fold_left (fun acc (v,v_ar) ->
+ acc >> (single_AC_factor s v v_ar)
+ )
+ (return t)
+ l
+
+
+
+(************)
+(* Matching *)
+(************)
+
+
+
+(** {!matching} is the generic matching judgement. Each time a
+ non-deterministic split is made, we have to come back to this one.
+
+ {!matchingSym} is used to match two applications that have the
+ same (free) head-symbol.
+
+ {!matchingAC} is used to match two sums (with the subtlety that
+ [x+y] matches [f a] which is a function application or [a*b] which
+ is a product).
+
+ {!matchingA} is used to match two products (with the subtlety that
+ [x*y] matches [f a] which is a function application, or [a+b]
+ which is a sum).
+
+
+*)
+let matching ?strict =
+ let rec matching env (p : nf_term) (t: nf_term) : Subst.t Search.m=
+ match p with
+ | TAC (s,l) ->
+ let l = linear l in
+ matchingAC env s l (get_AC s t)
+ | TA (s,l) ->
+ matchingA env s l (get_A s t)
+ | TSym (s,l) ->
+ (get_Sym s t)
+ >> (fun t -> matchingSym env l t)
+ | TVar x ->
+ begin match Subst.find env x with
+ | None -> return (Subst.add env x t)
+ | Some v -> if nf_equal v t then return env else fail ()
+ end
+
+ and
+ matchingAC (env : Subst.t) (current : symbol) (l : nf_term list) (t : (nf_term *int) list) =
+ match l with
+ | TSym (s,v):: q ->
+ pick_sym s t
+ >> (fun (v',t') ->
+ matchingSym env v v'
+ >> (fun env -> matchingAC env current q t'))
+
+ | TAC (s,v)::q when s = current ->
+ assert false
+ | TVar x:: q ->
+ begin match Subst.find env x with
+ | None ->
+ (var_ac_nondet_split ?strict current env x t)
+ >> (fun (env,residual) -> matchingAC env current q residual)
+ | Some v ->
+ (factor_AC current v t)
+ >> (fun residual -> matchingAC env current q residual)
+ end
+ | h :: q ->(* PAC =/= curent or PA *)
+ (ac_nondet_split t)
+ >>
+ (fun (left,right) ->
+ matching env h (mk_TAC current left)
+ >>
+ (
+ fun env ->
+ matchingAC env current q right
+ )
+ )
+ | [] -> if t = [] then return env else fail ()
+ and
+ matchingA (env : Subst.t) (current : symbol) (l : nf_term list) (t : nf_term list) =
+ match l with
+ | TSym (s,v) :: l ->
+ begin match t with
+ | TSym (s',v') :: r when s = s' ->
+ (matchingSym env v v')
+ >> (fun env -> matchingA env current l r)
+ | _ -> fail ()
+ end
+ | TA (s,v) :: l when s = current ->
+ assert false
+ | TVar x :: l ->
+ begin match Subst.find env x with
+ | None ->
+ var_a_nondet_split ?strict env current x t
+ >> (fun (env,residual)-> matchingA env current l residual)
+ | Some v ->
+ (left_factor current v t)
+ >> (fun residual -> matchingA env current l residual)
+ end
+ | h :: l ->
+ a_nondet_split t
+ >> (fun (t,r) ->
+ matching env h (mk_TA current t)
+ >> (fun env -> matchingA env current l r)
+ )
+ | [] -> if t = [] then return env else fail ()
+ and
+ matchingSym (env : Subst.t) (l : nf_term list) (t : nf_term list) =
+ List.fold_left2
+ (fun acc p t -> acc >> (fun env -> matching env p t))
+ (return env)
+ l
+ t
+
+ in
+ matching
+
+
+
+(***********)
+(* Subterm *)
+(***********)
+
+(** [tri_fold f l acc] folds on the list [l] and give to f the
+ beginning of the list in reverse order, the considered element, and
+ the last part of the list
+
+ as an exemple, on the list [1;2;3;4], we get the trace
+ f () [] 1 [2; 3; 4]
+ f () [1] 2 [3; 4]
+ f () [2;1] 3 [ 4]
+ f () [3;2;1] 4 []
+
+ it is the duty of the user to reverse the front if needed
+*)
+
+let tri_fold f (l : 'a list) (acc : 'b)= match l with
+ [] -> acc
+ | _ ->
+ let _,_,acc = List.fold_left (fun acc (t : 'a) ->
+ let l,r,acc = acc in
+ let r = List.tl r in
+ l,r,f acc l t r
+ ) ([], l,acc) l
+ in acc
+
+
+(** [subterm] solves a sub-term pattern matching.
+
+ This function is more high-level than {!matcher}, thus takes {!t}
+ as arguments rather than terms in normal form {!nf_term}.
+
+ We use three mutually recursive functions {!subterm},
+ {!subterm_AC}, {!subterm_A} to find the matching subterm, making
+ non-deterministic choices to split the term into a context and an
+ intersting sub-term. Intuitively, the only case in which we have to
+ go in depth is when we are left with a sub-term that is atomic.
+
+ Indeed, rewriting [H: b = c |- a+b+a = a+a+c], we do not want to
+ find recursively the sub-terms of [a+b] and [b+a], since they will
+ overlap with the sub-terms of [a+b+a].
+
+ We rebuild the context on the fly, leaving the variables in the
+ pattern uninstantiated. We do so in order to allow interaction
+ with the user, to choose the env.
+
+ Strange patterms like x*y*x can be instanciated by nothing, inside
+ a product. Therefore, we need to check that all the term is not
+ going into the context (hence the tests on the length of the
+ lists). With proper support for interaction with the user, we
+ should lift these tests. However, at the moment, they serve as
+ heuristics to return "interesting" matchings
+
+*)
+
+let return_non_empty raw_p m =
+ if Search.is_empty m
+ then
+ fail ()
+ else
+ return (raw_p ,m)
+
+let subterm ?strict (raw_p:t) (raw_t:t): (int* t * Subst.t m) m=
+ let p = term_of_t raw_p in
+ let t = term_of_t raw_t in
+ let rec subterm (t:nf_term) : (t * Subst.t m) m=
+ match t with
+ | TAC (s,l) ->
+ (ac_nondet_split l) >>
+ (fun (left,right) ->
+ (subterm_AC s left) >>
+ (fun (p,m) ->
+ let p = if right = [] then p else
+ Plus (s,p,t_of_term (mk_TAC s right))
+ in
+ return (p,m)
+ )
+
+ )
+ | TA (s,l) ->
+ (a_nondet_middle l)
+ >>
+ (fun (left, middle, right) ->
+ (subterm_A s middle) >>
+ (fun (p,m) ->
+ let p =
+ if right = [] then p else
+ Dot (s,p,t_of_term (mk_TA s right))
+ in
+ let p =
+ if left = [] then p else
+ Dot (s,t_of_term (mk_TA s left),p)
+ in
+ return (p,m)
+ )
+ )
+ | TSym (s, l) ->
+ let init = return_non_empty raw_p (matching ?strict Subst.empty p t) in
+ tri_fold (fun acc l t r ->
+ ((subterm t) >>
+ (fun (p,m) ->
+ let l = List.map t_of_term l in
+ let r = List.map t_of_term r in
+ let p = Sym (s, Array.of_list (List.rev_append l (p::r))) in
+ return (p,m)
+ )) >>| acc
+ ) l init
+ | _ -> assert false
+ and subterm_AC s tl =
+ match tl with
+ [x,1] -> subterm x
+ | _ ->
+ return_non_empty raw_p (matching ?strict Subst.empty p (mk_TAC s tl))
+ and subterm_A s tl =
+ match tl with
+ [x] -> subterm x
+ | _ ->
+ return_non_empty raw_p (matching ?strict Subst.empty p (mk_TA s tl))
+ in
+ (subterm t >> fun (p,m) -> return (Terms.size p,p,m))
+
+(* The functions we export, handlers for the previous ones. Some debug
+ information also *)
+let subterm ?strict raw t =
+ let sols = time (subterm ?strict raw) t "%fs spent in subterm (including matching)\n" in
+ if debug then Printf.printf "%i possible solution(s)\n"
+ (Search.fold (fun (_,_,envm) acc -> count envm + acc) sols 0);
+ sols
+
+
+let matcher ?strict p t =
+ let sols = time
+ (fun (p,t) ->
+ let p = (Terms.term_of_t p) in
+ let t = (Terms.term_of_t t) in
+ matching ?strict Subst.empty p t) (p,t)
+ "%fs spent in the matcher\n"
+ in
+ if debug then Printf.printf "%i solutions\n" (count sols);
+ sols
+
+(* A very basic way to interact with the envs, to choose a possible
+ solution *)
+open Pp
+let pp_env pt : Subst.t -> Pp.std_ppcmds = fun env ->
+ List.fold_left (fun acc (v,t) -> str (Printf.sprintf "x%i: " v) ++ pt t ++ str "; " ++ acc) (str "") (Subst.to_list env)
+
+let pp_envm pt : Subst.t Search.m -> Pp.std_ppcmds = fun m ->
+ let _,s = Search.fold
+ (fun env (n,acc) ->
+ n+1, h 0 (str (Printf.sprintf "%i:\t[" n) ++pp_env pt env ++ str "]") ++ fnl () :: acc
+ ) m (0,[]) in
+ List.fold_left (fun acc s -> s ++ acc) (str "") (s)
+
+let pp_all pt : (int * Terms.t * Subst.t Search.m) Search.m -> Pp.std_ppcmds = fun m ->
+ let _,s = Search.fold
+ (fun (size,context,envm) (n,acc) ->
+ let s = str (Printf.sprintf "subterm %i\t" n) in
+ let s = s ++ (str "(context ") ++ pt context ++ (str ")\n") in
+ let s = s ++ str (Printf.sprintf "\t%i possible(s) substitutions" (Search.count envm) ) ++ fnl () in
+ let s = s ++ pp_envm pt envm in
+ n+1, s::acc
+ ) m (0,[]) in
+ List.fold_left (fun acc s -> s ++ str "\n" ++ acc) (str "") (s)
+
diff --git a/matcher.mli b/matcher.mli
new file mode 100644
index 0000000..3e20e73
--- /dev/null
+++ b/matcher.mli
@@ -0,0 +1,192 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** Standalone module containing the algorithm for matching modulo
+ associativity and associativity and commutativity (AAC).
+
+ This module could be reused ouside of the Coq plugin.
+
+ Matching modulo AAC a pattern [p] against a term [t] boils down to
+ finding a substitution [env] such that the pattern [p] instantiated
+ with [env] is equal to [t] modulo AAC.
+
+ We proceed by structural decomposition of the pattern, trying all
+ possible non-deterministic split of the subject, when needed. The
+ function {!matcher} is limited to top-level matching, that is, the
+ subject must make a perfect match against the pattern ([x+x] does
+ not match [a+a+b] ).
+
+ We use a search monad {!Search} to perform non-deterministic
+ choices in an almost transparent way.
+
+ We also provide a function {!subterm} for finding a match that is
+ a subterm of the subject modulo AAC. In particular, this function
+ gives a solution to the aforementioned case ([x+x] against
+ [a+b+a]).
+*)
+
+(** {2 Utility functions} *)
+
+type symbol = int
+type var = int
+
+(** The {!Search} module contains a search monad that allows to
+ express our non-deterministic and back-tracking algorithm in a
+ legible maner.
+
+ @see <http://spivey.oriel.ox.ac.uk/mike/search-jfp.pdf> the
+ inspiration of this module
+*)
+module Search :
+sig
+ (** A data type that represent a collection of ['a] *)
+ type 'a m
+ (** bind and return *)
+ val ( >> ) : 'a m -> ('a -> 'b m) -> 'b m
+ val return : 'a -> 'a m
+ (** non-deterministic choice *)
+ val ( >>| ) : 'a m -> 'a m -> 'a m
+ (** failure *)
+ val fail : unit -> 'a m
+ (** folding through the collection *)
+ val fold : ('a -> 'b -> 'b) -> 'a m -> 'b -> 'b
+ (** derived facilities *)
+ val sprint : ('a -> string) -> 'a m -> string
+ val count : 'a m -> int
+ val choose : 'a m -> 'a option
+ val to_list : 'a m -> 'a list
+ val sort : ('a -> 'a -> int) -> 'a m -> 'a m
+ val is_empty: 'a m -> bool
+end
+
+(** The arguments of sums (or AC operators) are represented using finite multisets.
+ (Typically, [a+b+a] corresponds to [2.a+b], i.e. [Sum[a,2;b,1]]) *)
+type 'a mset = ('a * int) list
+
+(** [linear] expands a multiset into a simple list *)
+val linear : 'a mset -> 'a list
+
+(** Representations of expressions
+
+ The module {!Terms} defines two different types for expressions.
+ - a public type {!Terms.t} that represents abstract syntax trees
+ of expressions with binary associative and commutative operators
+ - a private type {!Terms.nf_term}, corresponding to a canonical
+ representation for the above terms modulo AAC. The construction
+ functions on this type ensure that these terms are in normal form
+ (that is, no sum can appear as a subterm of the same sum, no
+ trailing units, lists corresponding to multisets are sorted,
+ etc...).
+
+*)
+module Terms :
+sig
+
+ (** {2 Abstract syntax tree of terms and patterns}
+
+ We represent both terms and patterns using the following datatype.
+
+ Values of type [symbol] are used to index symbols. Typically,
+ given two associative operations [(^)] and [( * )], and two
+ morphisms [f] and [g], the term [f (a^b) (a*g b)] is represented
+ by the following value
+ [Sym(0,[| Dot(1, Sym(2,[||]), Sym(3,[||]));
+ Dot(4, Sym(2,[||]), Sym(5,[|Sym(3,[||])|])) |])]
+ where the implicit symbol environment associates
+ [f] to [0], [(^)] to [1], [a] to [2], [b] to [3], [( * )] to [4], and [g] to [5],
+
+ Accordingly, the following value, that contains "variables"
+ [Sym(0,[| Dot(1, Var x, Dot(4,[||]));
+ Dot(4, Var x, Sym(5,[|Sym(3,[||])|])) |])]
+ represents the pattern [forall x, f (x^1) (x*g b)], where [1] is the
+ unit associated with [( * )].
+ *)
+
+ type t =
+ Dot of (symbol * t * t)
+ | One of symbol
+ | Plus of (symbol * t * t)
+ | Zero of symbol
+ | Sym of (symbol * t array)
+ | Var of var
+
+ (** Test for equality of terms modulo AAC (relies on the following
+ canonical representation of terms) *)
+ val equal_aac : t -> t -> bool
+
+
+ (** {2 Normalised terms (canonical representation) }
+
+ A term in normal form is the canonical representative of the
+ equivalence class of all the terms that are equal modulo AAC
+ This representation is only used internally; it is exported here
+ for the sake of completeness *)
+
+ type nf_term
+
+ (** {3 Comparisons} *)
+
+ val nf_term_compare : nf_term -> nf_term -> int
+ val nf_equal : nf_term -> nf_term -> bool
+
+ (** {3 Printing function} *)
+
+ val sprint_nf_term : nf_term -> string
+
+ (** {3 Conversion functions} *)
+
+ (** we have the following property: [a] and [b] are equal modulo AAC
+ iif [nf_equal (term_of_t a) (term_of_t b) = true] *)
+ val term_of_t : t -> nf_term
+ val t_of_term : nf_term -> t
+
+end
+
+
+(** Substitutions (or environments)
+
+ The module {!Subst} contains infrastructure to deal with
+ substitutions, i.e., functions from variables to terms. Only a
+ restricted subsets of these functions need to be exported.
+
+ As expected, a particular substitution can be used to
+ instantiate a pattern.
+*)
+module Subst :
+sig
+ type t
+ val sprint : t -> string
+ val instantiate : t -> Terms.t-> Terms.t
+ val to_list : t -> (var*Terms.t) list
+end
+
+
+(** {2 Main functions exported by this module} *)
+
+(** [matcher p t] computes the set of solutions to the given top-level
+ matching problem ([p] is the pattern, [t] is the term). If the
+ [strict] flag is set, solutions where units are used to
+ instantiate some variables are excluded, unless this unit appears
+ directly under a function symbol (e.g., f(x) still matches f(1),
+ while x+x+y does not match a+b+c, since this would require to
+ assign 1 to x).
+*)
+val matcher : ?strict:bool -> Terms.t -> Terms.t -> Subst.t Search.m
+
+(** [subterm p t] computes a set of solutions to the given
+ subterm-matching problem.
+
+ @return a collection of possible solutions (each with the
+ associated depth, the context, and the solutions of the matching
+ problem). The context is actually a {!Terms.t} where the variables
+ are yet to be instantiated by one of the associated substitutions
+*)
+val subterm : ?strict:bool -> Terms.t -> Terms.t -> (int * Terms.t * Subst.t Search.m) Search.m
+
+(** pretty printing of the solutions *)
+val pp_all : (Terms.t -> Pp.std_ppcmds) -> (int * Terms.t * Subst.t Search.m) Search.m -> Pp.std_ppcmds
diff --git a/theory.ml b/theory.ml
new file mode 100644
index 0000000..45a76f1
--- /dev/null
+++ b/theory.ml
@@ -0,0 +1,708 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** Constr from the theory of this particular development
+
+ The inner-working of this module is highly correlated with the
+ particular structure of {b AAC.v}, therefore, it should
+ be of little interest to most readers.
+*)
+
+
+open Term
+
+let ac_theory_path = ["AAC"]
+
+module Sigma = struct
+ let sigma = lazy (Coq.init_constant ac_theory_path "sigma")
+ let sigma_empty = lazy (Coq.init_constant ac_theory_path "sigma_empty")
+ let sigma_add = lazy (Coq.init_constant ac_theory_path "sigma_add")
+ let sigma_get = lazy (Coq.init_constant ac_theory_path "sigma_get")
+
+ let add ty n x map =
+ mkApp (Lazy.force sigma_add,[|ty; n; x ; map|])
+ let empty ty =
+ mkApp (Lazy.force sigma_empty,[|ty |])
+ let typ ty =
+ mkApp (Lazy.force sigma, [|ty|])
+
+ let to_fun ty null map =
+ mkApp (Lazy.force sigma_get, [|ty;null;map|])
+
+ let of_list ty null l =
+ let map =
+ List.fold_left
+ (fun acc (i,t) -> assert (i > 0); add ty (Coq.Pos.of_int i) t acc)
+ (empty ty)
+ l
+ in to_fun ty null map
+end
+
+
+module Sym = struct
+ type pack = {ar: Term.constr; value: Term.constr ; morph: Term.constr}
+
+ let path = ac_theory_path @ ["Sym"]
+ let typ = lazy (Coq.init_constant path "pack")
+ let mkPack = lazy (Coq.init_constant path "mkPack")
+ let typeof = lazy (Coq.init_constant path "type_of")
+ let relof = lazy (Coq.init_constant path "rel_of")
+ let value = lazy (Coq.init_constant path "value")
+ let null = lazy (Coq.init_constant path "null")
+ let mk_typeof : Coq.reltype -> int -> constr = fun (x,r) n ->
+ mkApp (Lazy.force typeof, [| x ; Coq.Nat.of_int n |])
+ let mk_relof : Coq.reltype -> int -> constr = fun (x,r) n ->
+ mkApp (Lazy.force relof, [| x;r ; Coq.Nat.of_int n |])
+ let mk_pack ((x,r): Coq.reltype) s =
+ mkApp (Lazy.force mkPack, [|x;r; s.ar;s.value;s.morph|])
+ let null eqt witness =
+ let (x,r),e = eqt in
+ mkApp (Lazy.force null, [| x;r;e;witness |])
+
+end
+
+module Sum = struct
+ type pack = {plus: Term.constr; zero: Term.constr ; opac: Term.constr}
+
+ let path = ac_theory_path @ ["Sum"]
+ let typ = lazy (Coq.init_constant path "pack")
+ let mkPack = lazy (Coq.init_constant path "mkPack")
+ let cstr_zero = lazy (Coq.init_constant path "zero")
+ let cstr_plus = lazy (Coq.init_constant path "plus")
+
+ let mk_pack: Coq.reltype -> pack -> Term.constr = fun (x,r) s ->
+ mkApp (Lazy.force mkPack , [| x ; r; s.plus; s.zero; s.opac|])
+
+ let zero: Coq.reltype -> constr -> constr = fun (x,r) pack ->
+ mkApp (Lazy.force cstr_zero, [| x;r;pack|])
+ let plus: Coq.reltype -> constr -> constr = fun (x,r) pack ->
+ mkApp (Lazy.force cstr_plus, [| x;r;pack|])
+
+
+end
+
+module Prd = struct
+ type pack = {dot: Term.constr; one: Term.constr ; opa: Term.constr}
+
+ let path = ac_theory_path @ ["Prd"]
+ let typ = lazy (Coq.init_constant path "pack")
+ let mkPack = lazy (Coq.init_constant path "mkPack")
+ let cstr_one = lazy (Coq.init_constant path "one")
+ let cstr_dot = lazy (Coq.init_constant path "dot")
+
+ let mk_pack: Coq.reltype -> pack -> Term.constr = fun (x,r) s ->
+ mkApp (Lazy.force mkPack , [| x ; r; s.dot; s.one; s.opa|])
+ let one: Coq.reltype -> constr -> constr = fun (x,r) pack ->
+ mkApp (Lazy.force cstr_one, [| x;r;pack|])
+ let dot: Coq.reltype -> constr -> constr = fun (x,r) pack ->
+ mkApp (Lazy.force cstr_dot, [| x;r;pack|])
+
+
+end
+
+
+let op_ac = lazy (Coq.init_constant ac_theory_path "Op_AC")
+let op_a = lazy (Coq.init_constant ac_theory_path "Op_A")
+
+(** The constants from the inductive type *)
+let _Tty = lazy (Coq.init_constant ac_theory_path "T")
+let vTty = lazy (Coq.init_constant ac_theory_path "vT")
+
+let rsum = lazy (Coq.init_constant ac_theory_path "sum")
+let rprd = lazy (Coq.init_constant ac_theory_path "prd")
+let rsym = lazy (Coq.init_constant ac_theory_path "sym")
+let vnil = lazy (Coq.init_constant ac_theory_path "vnil")
+let vcons = lazy (Coq.init_constant ac_theory_path "vcons")
+let eval = lazy (Coq.init_constant ac_theory_path "eval")
+let decide_thm = lazy (Coq.init_constant ac_theory_path "decide")
+
+(** Rebuild an equality *)
+let mk_equal = fun (x,r) left right ->
+ mkApp (r, [| left; right|])
+
+
+let anomaly msg =
+ Util.anomaly ("aac_tactics: " ^ msg)
+
+let user_error msg =
+ Util.error ("aac_tactics: " ^ msg)
+
+
+module Trans = struct
+
+
+ (** {1 From Coq to Abstract Syntax Trees (AST)}
+
+ This module provides facilities to interpret a Coq term with
+ arbitrary operators as an abstract syntax tree. Considering an
+ application, we try to infer instances of our classes. We
+ consider that raw morphisms ([Sym]) are coarser than [A]
+ operators, which in turn are coarser than [AC] operators. We
+ need to treat the units first. Indeed, considering [S] as a
+ morphism is problematic because [S O] is a unit.
+
+ During this reification, we gather some informations that will
+ be used to rebuild Coq terms from AST ( type {!envs}) *)
+
+ type pack =
+ | AC of Sum.pack (* the opac pack *)
+ | A of Prd.pack (* the opa pack *)
+ | Sym of Sym.pack (* the sym pack *)
+
+ type head =
+ | Fun
+ | Plus
+ | Dot
+ | One
+ | Zero
+
+
+ (* discr is a map from {!Term.constr} to
+ (head * pack).
+
+ [None] means that it is not possible to instantiate this partial
+ application to an interesting class.
+
+ [Some x] means that we found something in the past. This means in
+ particular that a single constr cannot be two things at the same
+ time.
+
+ However, for the transitivity process, we do not need to remember
+ if the constr was the unit or the binary operation. We will use
+ the projections from the module's record.
+
+ The field [bloom] allows to give a unique number to each class we
+ found. *)
+ type envs =
+ {
+ discr : (constr , (head * pack) option) Hashtbl.t ;
+
+ bloom : (pack, int ) Hashtbl.t;
+ bloom_back : (int, pack ) Hashtbl.t;
+ bloom_next : int ref;
+ }
+
+ let empty_envs () =
+ {
+ discr = Hashtbl.create 17;
+
+ bloom = Hashtbl.create 17;
+ bloom_back = Hashtbl.create 17;
+ bloom_next = ref 1;
+ }
+
+ let add_bloom envs pack =
+ if Hashtbl.mem envs.bloom pack
+ then ()
+ else
+ let x = ! (envs.bloom_next) in
+ Hashtbl.add envs.bloom pack x;
+ Hashtbl.add envs.bloom_back x pack;
+ incr (envs.bloom_next)
+
+ let find_bloom envs pack =
+ try Hashtbl.find envs.bloom pack
+ with Not_found -> assert false
+
+
+ (** try to infer the kind of operator we are dealing with *)
+ let is_morphism rlt papp ar goal =
+ let typeof = Sym.mk_typeof rlt ar in
+ let relof = Sym.mk_relof rlt ar in
+ let env = Tacmach.pf_env goal in
+ let evar_map = Tacmach.project goal in
+ let m = Coq.Classes.mk_morphism typeof relof papp in
+ try
+ let _ = Tacmach.pf_check_type goal papp typeof in
+ let evar_map,m = Typeclasses.resolve_one_typeclass env evar_map m in
+ let pack = {Sym.ar = (Coq.Nat.of_int ar); Sym.value= papp; Sym.morph= m} in
+ Some (Coq.goal_update goal evar_map, Fun , Sym pack)
+ with
+ | Not_found -> None
+ | _ -> None
+
+ (** gives back the class *)
+ let is_op gl (rlt: Coq.reltype) pack op null =
+ let (x,r) = rlt in
+ let ty = mkApp (pack ,[|x;r; op; null |]) in
+ let env = Tacmach.pf_env gl in
+ let evar_map = Tacmach.project gl in
+ try
+ let em,t = Typeclasses.resolve_one_typeclass env evar_map ty in
+ let op = Evarutil.nf_evar em op in
+ let null = Evarutil.nf_evar em null in
+ Some (Coq.goal_update gl em,t,op,null)
+ with Not_found -> None
+
+
+ let is_plus (rlt: Coq.reltype) plus goal=
+ let (zero,goal) = Coq.evar_unit goal rlt in
+ match is_op goal rlt (Lazy.force op_ac) plus zero
+ with
+ | None -> None
+ | Some (gl,s,plus,zero) ->
+ let pack = {Sum.plus=plus;Sum.zero=zero;Sum.opac=s} in
+ Some (gl, Plus , AC pack)
+
+ let is_dot rlt dot goal=
+ let (one,goal) = Coq.evar_unit goal rlt in
+ match is_op goal rlt (Lazy.force op_a) dot one
+ with
+ | None -> None
+ | Some (goal,s,dot,one) ->
+ let pack = {Prd.dot=dot;Prd.one=one;Prd.opa=s} in
+ Some (goal, Dot, A pack)
+
+ let is_zero rlt zero goal =
+ let (plus,goal) = Coq.evar_binary goal rlt in
+ match is_op goal rlt (Lazy.force op_ac) plus zero
+ with
+ | None -> None
+ | Some (goal,s,plus,zero) ->
+ let pack = {Sum.plus=plus;Sum.zero=zero;Sum.opac=s} in
+ Some (goal, Zero , AC pack)
+
+ let is_one rlt one goal =
+ let (dot,goal) = Coq.evar_binary goal rlt in
+ match is_op goal rlt (Lazy.force op_a) dot one
+ with
+ | None -> None
+ | Some (goal,s,dot,one)->
+ let pack = {Prd.dot=dot;Prd.one=one;Prd.opa=s} in
+ Some (goal, One, A pack)
+
+
+ let (>>) x y a =
+ match x a with | None -> y a | x -> x
+
+ let is_unit rlt papp =
+ (is_zero rlt papp )>> (is_one rlt papp)
+
+ let what_is_it rlt papp ar goal :
+ (Coq.goal_sigma * head * pack ) option =
+ match ar with
+ | 2 ->
+ begin
+ (
+ (is_plus rlt papp)>>
+ (is_dot rlt papp)>>
+ (is_morphism rlt papp ar)
+ ) goal
+ end
+ | _ ->
+ is_morphism rlt papp ar goal
+
+
+
+
+ (** [discriminates goal envs rlt t ca] infer :
+
+ - the nature of the application [t ca] (is the head symbol a
+ Plus, a Dot, so on),
+
+ - its {! pack } (is it an AC operator, or an A operator, or a
+ Symbol ?),
+
+ - the relevant partial application,
+
+ - the vector of the remaining arguments
+
+ We use an expansion to handle the special case of units, before
+ going back to the general discrimination procedure *)
+ let discriminate goal envs (rlt : Coq.reltype) t ca : Coq.goal_sigma * head * pack * constr * constr array =
+ let nb_params = Array.length ca in
+ let f cont x p_app ar =
+ assert (0 <= ar && ar <= nb_params);
+ match x with
+ | Some (goal, discr, pack) ->
+ Hashtbl.add envs.discr p_app (Some (discr, pack));
+ add_bloom envs pack;
+ (goal, discr, pack, p_app, Array.sub ca (nb_params-ar) ar)
+ | None ->
+ begin
+ (* to memoise the failures *)
+ Hashtbl.add envs.discr p_app None;
+ (* will terminate, since [const] is
+ capped, and it is easy to find an
+ instance of a constant *)
+ cont goal (ar-1)
+ end
+ in
+ let rec aux goal ar :Coq.goal_sigma * head * pack * constr * constr array =
+ assert (0 <= ar && ar <= nb_params);
+ let p_app = mkApp (t, Array.sub ca 0 (nb_params - ar)) in
+ begin
+ try
+ begin match Hashtbl.find envs.discr p_app with
+ | None -> aux goal (ar-1)
+ | Some (discr,sym) ->
+ goal,
+ discr,
+ sym,
+ p_app,
+ Array.sub ca (nb_params -ar ) ar
+ end
+ with
+ Not_found -> (* Could not find this constr *)
+ f aux (what_is_it rlt p_app ar goal) p_app ar
+ end
+ in
+ match is_unit rlt (mkApp (t,ca)) goal with
+ | None -> aux goal (nb_params)
+ | x -> f (fun _ -> assert false) x (mkApp (t,ca)) 0
+
+ let discriminate goal envs rlt x =
+ try
+ match Coq.decomp_term x with
+ | Var _ ->
+ discriminate goal envs rlt x [| |]
+ | App (t,ca) ->
+ discriminate goal envs rlt t ca
+ | Construct c ->
+ discriminate goal envs rlt x [| |]
+ | _ ->
+ assert false
+ with
+ (* TODO: is it the only source of invalid use that fall into
+ this catch_all ? *)
+ e -> user_error "cannot handle this kind of hypotheses (higher order function symbols, variables occuring under a non-morphism, etc)."
+
+
+ (** [t_of_constr goal rlt envs cstr] builds the abstract syntax
+ tree of the term [cstr]. Doing so, it modifies the environment
+ of known stuff [envs], and eventually creates fresh
+ evars. Therefore, we give back the goal updated accordingly *)
+ let t_of_constr goal (rlt: Coq.reltype ) envs : constr -> Matcher.Terms.t *Coq.goal_sigma =
+ let r_evar_map = ref (goal) in
+ let rec aux x =
+ match Coq.decomp_term x with
+ | Rel i -> Matcher.Terms.Var i
+ | _ ->
+ let goal, sym, pack , p_app, ca = discriminate (!r_evar_map) envs rlt x in
+ r_evar_map := goal;
+ let k = find_bloom envs pack
+ in
+ match sym with
+ | Plus ->
+ assert (Array.length ca = 2);
+ Matcher.Terms.Plus ( k, aux ca.(0), aux ca.(1))
+ | Zero ->
+ assert (Array.length ca = 0);
+ Matcher.Terms.Zero ( k)
+ | Dot ->
+ assert (Array.length ca = 2);
+ Matcher.Terms.Dot ( k, aux ca.(0), aux ca.(1))
+ | One ->
+ assert (Array.length ca = 0);
+ Matcher.Terms.One ( k)
+ | Fun ->
+ Matcher.Terms.Sym ( k, Array.map aux ca)
+ in
+ (
+ fun x -> let r = aux x in r, !r_evar_map
+ )
+
+ (** {1 From AST back to Coq }
+
+ The next functions allow one to map OCaml abstract syntax trees
+ to Coq terms *)
+
+ (** {2 Building raw, natural, terms} *)
+
+ (** [raw_constr_of_t] rebuilds a term in the raw representation *)
+ let raw_constr_of_t envs (rlt : Coq.reltype) (t : Matcher.Terms.t) : Term.constr =
+ let add_set,get =
+ let r = ref [] in
+ let rec add x = function
+ [ ] -> [x]
+ | t::q when t = x -> t::q
+ | t::q -> t:: (add x q)
+ in
+ (fun x -> r := add x !r),(fun () -> !r)
+ in
+ let rec aux t =
+ match t with
+ | Matcher.Terms.Plus (s,l,r) ->
+ begin match Hashtbl.find envs.bloom_back s with
+ | AC s ->
+ mkApp (s.Sum.plus , [|(aux l);(aux r)|])
+ | _ -> assert false
+ end
+ | Matcher.Terms.Dot (s,l,r) ->
+ begin match Hashtbl.find envs.bloom_back s with
+ | A s ->
+ mkApp (s.Prd.dot, [|(aux l);(aux r)|])
+ | _ -> assert false
+ end
+ | Matcher.Terms.Sym (s,t) ->
+ begin match Hashtbl.find envs.bloom_back s with
+ | Sym s ->
+ mkApp (s.Sym.value, Array.map aux t)
+ | _ -> assert false
+ end
+ | Matcher.Terms.One x ->
+ begin match Hashtbl.find envs.bloom_back x with
+ | A s ->
+ s.Prd.one
+ | _ -> assert false
+ end
+ | Matcher.Terms.Zero x ->
+ begin match Hashtbl.find envs.bloom_back x with
+ | AC s ->
+ s.Sum.zero
+ | _ -> assert false
+ end
+ | Matcher.Terms.Var i -> add_set i;
+ let i = (Names.id_of_string (Printf.sprintf "x%i" i)) in
+ mkVar (i)
+ in
+ let x = fst rlt in
+ let rec cap c = function [] -> c
+ | t::q -> let i = (Names.id_of_string (Printf.sprintf "x%i" t)) in
+ cap (mkNamedProd i x c) q
+ in
+ cap ((aux t)) (get ())
+ (* aux t is possible for printing only, but I wonder what
+ would be the status of such a term*)
+
+
+ (** {2 Building reified terms} *)
+
+ (* Some informations to be able to build the maps *)
+ type reif_params =
+ {
+ a_null : constr;
+ ac_null : constr;
+ sym_null : constr;
+ a_ty : constr;
+ ac_ty : constr;
+ sym_ty : constr;
+ }
+
+ type sigmas = {
+ env_sym : Term.constr;
+ env_sum : Term.constr;
+ env_prd : Term.constr;
+ }
+ (** A record containing the environments that will be needed by the
+ decision procedure, as a Coq constr. Contains also the functions
+ from the symbols (as ints) to indexes (as constr) *)
+ type sigma_maps = {
+ sym_to_pos : int -> Term.constr;
+ sum_to_pos : int -> Term.constr;
+ prd_to_pos : int -> Term.constr;
+ }
+
+ (* convert the [envs] into the [sigma_maps] *)
+ let envs_to_list rlt envs =
+ let add x y (n,l) = (n+1, (x, ( n, y))::l) in
+ let nil = 1,[]in
+ Hashtbl.fold
+ (fun int pack (sym,sum,prd) ->
+ match pack with
+ |AC s ->
+ let s = Sum.mk_pack rlt s in
+ sym, add (int) s sum, prd
+ |A s ->
+ let s = Prd.mk_pack rlt s in
+ sym, sum, add (int) s prd
+ | Sym s ->
+ let s = Sym.mk_pack rlt s in
+ add (int) s sym, sum, prd
+ )
+ envs.bloom_back
+ (nil,nil,nil)
+
+
+
+ (** infers some stuff that will be required when we will build
+ environments (our environments need a default case, so we need
+ an Op_AC, an Op_A, and a symbol) *)
+
+ (* Note : this function can fail if the user is using the wrong
+ relation, like proving a = b, while the classes are defined with
+ another relation (==)
+ *)
+ let build_reif_params goal eqt =
+ let rlt = fst eqt in
+ let plus,goal = Coq.evar_binary goal rlt in
+ let dot ,goal = Coq.evar_binary goal rlt in
+ let one ,goal = Coq.evar_unit goal rlt in
+ let zero,goal = Coq.evar_unit goal rlt in
+
+ let (x,r) = rlt in
+ let ty_ac = ( mkApp (Lazy.force op_ac, [| x;r;plus;zero|])) in
+ let ty_a = ( mkApp (Lazy.force op_a, [| x;r;dot;one|])) in
+ let a ,goal=
+ try Coq.resolve_one_typeclass goal ty_a
+ with Not_found -> user_error ("Unable to infer a default A operator.")
+ in
+ let ac ,goal=
+ try Coq.resolve_one_typeclass goal ty_ac
+ with Not_found -> user_error ("Unable to infer a default AC operator.")
+ in
+ let plus = Coq.nf_evar goal plus in
+ let dot = Coq.nf_evar goal dot in
+ let one = Coq.nf_evar goal one in
+ let zero = Coq.nf_evar goal zero in
+
+ let record =
+ {a_null = Prd.mk_pack rlt {Prd.dot=dot;Prd.one=one;Prd.opa=a};
+ ac_null= Sum.mk_pack rlt {Sum.plus=plus;Sum.zero=zero;Sum.opac=ac};
+ sym_null = Sym.null eqt zero;
+ a_ty = (mkApp (Lazy.force Prd.typ, [| x;r; |]));
+ ac_ty = ( mkApp (Lazy.force Sum.typ, [| x;r|]));
+ sym_ty = mkApp (Lazy.force Sym.typ, [|x;r|])
+ }
+ in
+ goal, record
+
+ (** [build_sigma_maps] given a envs and some reif_params, we are
+ able to build the sigmas *)
+ let build_sigma_maps goal eqt envs : sigmas * sigma_maps * Proof_type.goal Evd.sigma =
+ let rlt = fst eqt in
+ let goal,rp = build_reif_params goal eqt in
+ let ((_,sym), (_,sum),(_,prd)) = envs_to_list rlt envs in
+ let f = List.map (fun (x,(y,z)) -> (x,Coq.Pos.of_int y)) in
+ let g = List.map (fun (x,(y,z)) -> (y,z)) in
+ let sigmas =
+ {env_sym = Sigma.of_list rp.sym_ty rp.sym_null (g sym);
+ env_sum = Sigma.of_list rp.ac_ty rp.ac_null (g sum);
+ env_prd = Sigma.of_list rp.a_ty rp.a_null (g prd);
+ } in
+ let sigma_maps = {
+ sym_to_pos = (let sym = f sym in fun x -> (List.assoc x sym));
+ sum_to_pos = (let sum = f sum in fun x -> (List.assoc x sum));
+ prd_to_pos = (let prd = f prd in fun x -> (List.assoc x prd));
+ }
+ in
+ sigmas, sigma_maps , goal
+
+
+
+ (** builders for the reification *)
+ type reif_builders =
+ {
+ rsum: constr -> constr -> constr -> constr;
+ rprd: constr -> constr -> constr -> constr;
+ rzero: constr -> constr;
+ rone: constr -> constr;
+ rsym: constr -> constr array -> constr
+ }
+
+ (* donne moi une tactique, je rajoute ma part. Potentiellement, il
+ est possible d'utiliser la notation 'do' a la Haskell:
+ http://www.cas.mcmaster.ca/~carette/pa_monad/ *)
+ let (>>) : 'a * Proof_type.tactic -> ('a -> 'b * Proof_type.tactic) -> 'b * Proof_type.tactic =
+ fun (x,t) f ->
+ let b,t' = f x in
+ b, Tacticals.tclTHEN t t'
+
+ let return x = x, Tacticals.tclIDTAC
+
+ let mk_vect vnil vcons v =
+ let ar = Array.length v in
+ let rec aux = function
+ | 0 -> vnil
+ | n -> let n = n-1 in
+ mkApp( vcons,
+ [|
+ (Coq.Nat.of_int n);
+ v.(ar - 1 - n);
+ (aux (n))
+ |]
+ )
+ in aux ar
+
+ (* TODO: use a do notation *)
+ let mk_reif_builders (rlt: Coq.reltype) (env_sym:constr) :reif_builders*Proof_type.tactic =
+ let x,r = rlt in
+ let tty = mkApp (Lazy.force _Tty, [| x ; r ; env_sym|]) in
+ (Coq.mk_letin' "mk_rsum" (mkApp (Lazy.force rsum, [| x; r; env_sym|]))) >>
+ (fun rsum -> (Coq.mk_letin' "mk_rprd" (mkApp (Lazy.force rprd, [| x; r; env_sym|])))
+ >> fun rprd -> ( Coq.mk_letin' "mk_rsym" (mkApp (Lazy.force rsym, [| x;r; env_sym|])))
+ >> fun rsym -> (Coq.mk_letin' "mk_vnil" (mkApp (Lazy.force vnil, [| x;r; env_sym|])))
+ >> fun vnil -> Coq.mk_letin' "mk_vcons" (mkApp (Lazy.force vcons, [| x;r; env_sym|]))
+ >> fun vcons ->
+ return {
+ rsum =
+ begin fun idx l r ->
+ mkApp (rsum, [| idx ; Coq.mk_mset tty [l,1 ; r,1]|])
+ end;
+ rzero =
+ begin fun idx ->
+ mkApp (rsum, [| idx ; Coq.mk_mset tty []|])
+ end;
+ rprd =
+ begin fun idx l r ->
+ let lst = Coq.List.of_list tty [l;r] in
+ mkApp (rprd, [| idx; lst|])
+ end;
+ rone =
+ begin fun idx ->
+ let lst = Coq.List.of_list tty [] in
+ mkApp (rprd, [| idx; lst|]) end;
+ rsym =
+ begin fun idx v ->
+ let vect = mk_vect vnil vcons v in
+ mkApp (rsym, [| idx; vect|])
+ end;
+ }
+ )
+
+
+
+ type reifier = sigma_maps * reif_builders
+
+ let mk_reifier eqt envs goal : sigmas * reifier * Proof_type.tactic =
+ let s, sm, goal = build_sigma_maps goal eqt envs in
+ let env_sym, env_sym_letin = Coq.mk_letin "env_sym_name" (s.env_sym) in
+ let env_prd, env_prd_letin = Coq.mk_letin "env_prd_name" (s.env_prd) in
+ let env_sum, env_sum_letin = Coq.mk_letin "env_sum_name" (s.env_sum) in
+ let s = {env_sym = env_sym; env_prd = env_prd; env_sum = env_sum} in
+ let rb , tac = mk_reif_builders (fst eqt) (s.env_sym) in
+ let evar_map = Tacmach.project goal in
+ let tac = Tacticals.tclTHENLIST (
+ [ Refiner.tclEVARS evar_map;
+ env_prd_letin ;
+ env_sum_letin ;
+ env_sym_letin ;
+ tac
+ ]) in
+ s, (sm, rb), tac
+
+
+ (** [reif_constr_of_t reifier t] rebuilds the term [t] in the
+ reified form. We use the [reifier] to minimise the size of the
+ terms (we make as much lets as possible)*)
+ let reif_constr_of_t (sm,rb) (t:Matcher.Terms.t) : constr =
+ let rec aux = function
+ | Matcher.Terms.Plus (s,l,r) ->
+ let idx = sm.sum_to_pos s in
+ rb.rsum idx (aux l) (aux r)
+ | Matcher.Terms.Dot (s,l,r) ->
+ let idx = sm.prd_to_pos s in
+ rb.rprd idx (aux l) (aux r)
+ | Matcher.Terms.Sym (s,t) ->
+ let idx = sm.sym_to_pos s in
+ rb.rsym idx (Array.map aux t)
+ | Matcher.Terms.One s ->
+ let idx = sm.prd_to_pos s in
+ rb.rone idx
+ | Matcher.Terms.Zero s ->
+ let idx = sm.sum_to_pos s in
+ rb.rzero idx
+ | Matcher.Terms.Var i ->
+ anomaly "call to reif_constr_of_t on a term with variables."
+ in aux t
+
+
+end
+
+
+
diff --git a/theory.mli b/theory.mli
new file mode 100644
index 0000000..85ad24b
--- /dev/null
+++ b/theory.mli
@@ -0,0 +1,219 @@
+(***************************************************************************)
+(* This is part of aac_tactics, it is distributed under the terms of the *)
+(* GNU Lesser General Public License version 3 *)
+(* (see file LICENSE for more details) *)
+(* *)
+(* Copyright 2009-2010: Thomas Braibant, Damien Pous. *)
+(***************************************************************************)
+
+(** Bindings for Coq constants that are specific to the plugin;
+ reification and translation functions.
+
+ Note: this module is highly correlated with the definitions of {i
+ AAC.v}.
+
+ This module interfaces with the above Coq module; it provides
+ facilities to interpret a term with arbitrary operators as an
+ abstract syntax tree, and to convert an AST into a Coq term
+ (either using the Coq "raw" terms, as written in the starting
+ goal, or using the reified Coq datatypes we define in {i
+ AAC.v}).
+*)
+
+
+
+
+(** Environments *)
+module Sigma:
+sig
+ (** [add ty n x map] adds the value [x] of type [ty] with key [n] in [map] *)
+ val add: Term.constr ->Term.constr ->Term.constr ->Term.constr ->Term.constr
+
+ (** [empty ty] create an empty map of type [ty] *)
+ val empty: Term.constr ->Term.constr
+
+ (** [of_list ty null l] translates an OCaml association list into a Coq one *)
+ val of_list: Term.constr -> Term.constr -> (int * Term.constr ) list -> Term.constr
+
+ (** [to_fun ty null map] converts a Coq association list into a Coq function (with default value [null]) *)
+ val to_fun: Term.constr ->Term.constr ->Term.constr ->Term.constr
+end
+
+(** The constr associated with our typeclasses for AC or A operators *)
+val op_ac:Term.constr lazy_t
+val op_a:Term.constr lazy_t
+
+(** The evaluation fonction, used to convert a reified coq term to a
+ raw coq term *)
+val eval: Term.constr lazy_t
+
+(** The main lemma of our theory, that is
+ [compare (norm u) (norm v) = Eq -> eval u == eval v] *)
+val decide_thm:Term.constr lazy_t
+
+(** {2 Packaging modules} *)
+
+(** Dynamically typed morphisms *)
+module Sym:
+sig
+ (** mimics the Coq record [Sym.pack] *)
+ type pack = {ar: Term.constr; value: Term.constr ; morph: Term.constr}
+
+ val typ: Term.constr lazy_t
+
+ (** [mk_typeof rlt int] *)
+ val mk_typeof: Coq.reltype ->int ->Term.constr
+
+ (** [mk_typeof rlt int] *)
+ val mk_relof: Coq.reltype ->int ->Term.constr
+
+ (** [mk_pack rlt (ar,value,morph)] *)
+ val mk_pack: Coq.reltype -> pack -> Term.constr
+
+ (** [null] builds a dummy symbol, given an {!Coq.eqtype} and a
+ constant *)
+ val null: Coq.eqtype -> Term.constr -> Term.constr
+
+end
+
+(** Dynamically typed AC operations *)
+module Sum:
+sig
+
+ (** mimics the Coq record [Sum.pack] *)
+ type pack = {plus : Term.constr; zero: Term.constr ; opac: Term.constr}
+
+ val typ: Term.constr lazy_t
+
+ (** [mk_pack rlt (plus,zero,opac)] *)
+ val mk_pack: Coq.reltype -> pack -> Term.constr
+
+ (** [zero rlt pack]*)
+ val zero: Coq.reltype -> Term.constr -> Term.constr
+
+ (** [plus rlt pack]*)
+ val plus: Coq.reltype -> Term.constr -> Term.constr
+
+end
+
+
+(** Dynamically typed A operations *)
+module Prd:
+sig
+
+ (** mimics the Coq record [Prd.pack] *)
+ type pack = {dot: Term.constr; one: Term.constr ; opa: Term.constr}
+
+ val typ: Term.constr lazy_t
+
+ (** [mk_pack rlt (dot,one,opa)] *)
+ val mk_pack: Coq.reltype -> pack -> Term.constr
+
+ (** [one rlt pack]*)
+ val one: Coq.reltype -> Term.constr -> Term.constr
+
+ (** [dot rlt pack]*)
+ val dot: Coq.reltype -> Term.constr -> Term.constr
+
+end
+
+
+(** [mk_equal rlt l r] builds the term [l == r] *)
+val mk_equal : Coq.reltype -> Term.constr -> Term.constr -> Term.constr
+
+
+(** {2 Building reified terms}
+
+ We define a bundle of functions to build reified versions of the
+ terms (those that will be given to the reflexive decision
+ procedure). In particular, each field takes as first argument the
+ index of the symbol rather than the symbol itself. *)
+
+(** Tranlations between Coq and OCaml *)
+module Trans : sig
+
+ (** This module provides facilities to interpret a term with
+ arbitrary operators as an instance of an abstract syntax tree
+ {!Matcher.Terms.t}.
+
+ For each Coq application [f x_1 ... x_n], this amounts to
+ deciding whether one of the partial applications [f x_1
+ ... x_i], [i<=n] is a proper morphism, whether the partial
+ application with [i=n-2] yields an A or AC binary operator, and
+ whether the whole term is the unit for some A or AC operator. We
+ use typeclass resolution to test each of these possibilities.
+
+ Note that there are ambiguous terms:
+ - a term like [f x y] might yield a unary morphism ([f x]) and a
+ binary one ([f]); we select the latter one (unless [f] is A or
+ AC, in which case we declare it accordingly);
+ - a term like [S O] can be considered as a morphism ([S])
+ applied to a unit for [(+)], or as a unit for [( * )]; we
+ chose to give priority to units, so that the latter
+ interpretation is selected in this case;
+ - an element might be the unit for several operations; the
+ behaviour in this case is almost unspecified: we simply give
+ priority to AC operations.
+ *)
+
+
+ (** To achieve this reification, one need to record informations
+ about the collected operators (symbols, binary operators,
+ units). We use the following imperative internal data-structure to
+ this end. *)
+
+ type envs
+ val empty_envs : unit -> envs
+
+ (** {1 Reification: from Coq terms to AST {!Matcher.Terms.t} } *)
+
+ (** [t_of_constr goal rlt envs cstr] builds the abstract syntax tree
+ of the term [cstr]. We rely on the [goal] to perform typeclasses
+ resolutions to find morphisms compatible with the relation
+ [rlt]. Doing so, it modifies the reification environment
+ [envs]. Moreover, we need to create creates fresh evars; this is
+ why we give back the [goal], accordingly updated. *)
+ val t_of_constr : Coq.goal_sigma -> Coq.reltype -> envs -> Term.constr -> Matcher.Terms.t * Coq.goal_sigma
+
+ (** {1 Reconstruction: from AST back to Coq terms }
+
+ The next functions allow one to map OCaml abstract syntax trees
+ to Coq terms. We need two functions to rebuild different kind of
+ terms: first, raw terms, like the one encountered by
+ {!t_of_constr}; second, reified Coq terms, that are required for
+ the reflexive decision procedure. *)
+
+ (** {2 Building raw, natural, terms} *)
+
+ (** [raw_constr_of_t] rebuilds a term in the raw representation *)
+ val raw_constr_of_t : envs -> Coq.reltype -> Matcher.Terms.t -> Term.constr
+
+ (** {2 Building reified terms} *)
+
+ (** The reification environments, as Coq constrs *)
+ type sigmas = {
+ env_sym : Term.constr;
+ env_sum : Term.constr;
+ env_prd : Term.constr;
+ }
+
+ (** We need to reify two terms (left and right members of a goal)
+ that share the same reification envirnoment. Therefore, we need
+ to add letins to the proof context in order to ensure some
+ sharing in the proof terms we produce.
+
+ Moreover, in order to have as much sharing as possible, we also
+ add letins for various partial applications that are used
+ throughout the terms.
+
+ To achieve this, we decompose the reconstruction function into
+ two steps: first, we build the reification environment and then
+ reify each term successively.*)
+ type reifier
+
+ val mk_reifier : Coq.eqtype -> envs -> Coq.goal_sigma -> sigmas * reifier * Proof_type.tactic
+
+ (** [reif_constr_of_t reifier t] rebuilds the term [t] in the
+ reified form. *)
+ val reif_constr_of_t : reifier -> Matcher.Terms.t -> Term.constr
+end