summaryrefslogtreecommitdiff
path: root/AAC.v
diff options
context:
space:
mode:
Diffstat (limited to 'AAC.v')
-rw-r--r--AAC.v689
1 files changed, 689 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".