(** A library of positive multivariate polynomials *)
(** * Representation of polynomials:  - [pow] is the type of a variable identifier and its exponent.  - [mon] is the type of a monomial, consisting of a coefficient and a list of variable identifiers with their exponents.  - [pol] is the type of a polynomial, consisting of the number of variables and a list of monomials. *)
Definition pow : Type := (nat*nat)%type.
Definition mon : Type := (nat * list pow)%type.
Definition pol : Type := (nat * list mon)%type.
(** * Evaluation of polynomials  - [peval p [v0, v1,...]] is the evaluation of the polynomial [p] with variables [0], [1]... respectively instantiated with the values [v0], [v1]... If a variable is not assigned a value, then it is assumed to be [0]. Values that do not correspond to a variable are ignored. *)
Definition peval_pow (xn:pow)(l:list nat) : nat :=
power (nth (fst xn) l 0) (snd xn).
Definition peval_mon (m:mon)(l:list nat) : nat :=
(fst m) * multl (map (fun x => peval_pow x l) (snd m)).
Definition peval (p:pol)(l:list nat) :=
plusl (map (fun m => peval_mon m l) (snd p)).
Notation parity := (@fst nat (list mon)).
(** * Well-formedness of polynomials *)
Definition pWF_pow (ar:nat)(xn:pow) : Prop :=
fst xn < ar.
Definition pWF_mon (ar:nat)(m:mon) : Prop :=
andl (pWF_pow ar) (snd m).
Definition pWF' (ar:nat)(ml:list mon) : Prop :=
andl (pWF_mon ar) ml.
Definition pWF (p:pol) : Prop :=
pWF' (fst p) (snd p).
(** * Basic polynomials *)
(** ** Constant polynomial  - [pcst ar a] is the constant polynomial with [ar] variables and equal to the constant [a]. *)
Definition pcst (ar a:nat) : pol :=
(ar, [(a,nil)]).
(** ** Sum of polynomials  - [pplus p1 p2] is the sum of the polynomials [p1] and [p2].  - [pplusl pl] is the sum of the polynomials in the list [pl]. *)
Definition pplus (p1 p2:pol) : pol :=
(max (fst p1) (fst p2), snd p1 ++ snd p2).
Definition pplusl (pl:list pol) : pol :=
fold_right pplus (pcst 0 0) pl.
Definition pWF_pplusl : forall pl,
andl pWF pl -> pWF (pplusl pl).
(** ** Multiplication of polynomials  - [pmult p1 p2] is the multiplication of the polynomials [p1] and [p2].  - [pmultl pl] is the multiplication of the polynomials in the list [pl]. *)
Definition pmult_mon (m12:mon*mon) : mon :=
(fst (fst m12) * fst (snd m12), snd (fst m12) ++  snd (snd m12)).
Definition pmult (p1 p2:pol) : pol :=
(max (fst p1) (fst p2), map pmult_mon (list_prod (snd p1) (snd p2))).
Definition pmultl (pl:list pol) : pol :=
fold_right pmult (pcst 0 1) pl.
Definition pWF_pmultl pl :
andl pWF pl -> pWF (pmultl pl).
(** ** Power of a polynomial  - [ppower p n] is the polynomial [p] to the power [n]. *)
Fixpoint ppower (p:pol)(n:nat) : pol :=
match n with
| 0 => pcst (fst p) 1
| S n' => pmult p (ppower p n')
end.
(** ** Composition of polynomials  - [pcomp p pl] is the polynomial [p] where each variable [i] is replaced by the [i]th polynomial in the list [pl]. *)
Definition pcomp_pow' (xn:pow)(pl:list pol) : pol :=
ppower (nth (fst xn) pl (pcst 0 0)) (snd xn).
Definition pcomp_pow (xn:pow)(pl:list pol) : pol :=
(maxl (map parity pl), snd (pcomp_pow' xn pl)).
Definition pcomp_mon' (m:mon)(pl:list pol) : pol :=
pscalar (fst m) (pmultl (map (fun xn => pcomp_pow xn pl) (snd m))).
Definition pcomp_mon (m:mon)(pl:list pol) : pol :=
(maxl (map parity pl), snd (pcomp_mon' m pl)).
Definition pcomp' (p:pol)(pl:list pol) : pol :=
pplusl (map (fun m => pcomp_mon m pl) (snd p)).
Definition pcomp (p:pol)(pl:list pol) : pol :=
(maxl (map parity pl), snd (pcomp' p pl)).
(** ** Shifting of a polynomial  - [pshift p] is the polynomial [p] with one more variable and where each variable [i] is replaced by [i+1]. *)
Definition pshift_pow (xn:pow) : pow :=
(S (fst xn), snd xn).
Definition pshift_mon (m:mon) : mon :=
(fst m, map pshift_pow (snd m)).
Definition pshift (p:pol) : pol :=
(S (fst p), map pshift_mon (snd p)).
(** ** Polynomial defined as a sum of a range of variables  - [psum start len] is the polynomial with [start+len] variables and consisting in the sum of the variables [start] to [start+len-1]. *)
Definition psum (start len : nat) : pol :=
pplus (pcst (start+len) 0) (pplusl (map (pproj (start+len)) (seq start len))).

Lemma psum_correct start len l :
peval (psum start len) l =
plusl (map (fun i => nth i l 0) (seq start len)).