(* (c) Copyright 2006-2016 Microsoft Corporation and Inria.                  *)
(* Distributed under the terms of CeCILL-B.                                  *)
From mathcomp
From mathcomp
(******************************************************************************)
(* This file provides a generic definition for iterating an operator over a   *)
(* set of indices (bigop); this big operator is parametrized by the return    *)
(* type (R), the type of indices (I), the operator (op), the default value on *)
(* empty lists (idx), the range of indices (r), the filter applied on this    *)
(* range (P) and the expression we are iterating (F). The definition is not   *)
(* to be used directly, but via the wide range of notations provided and      *)
(* and which support a natural use of big operators.                          *)
(*   To improve performance of the Coq typechecker on large expressions, the  *)
(* bigop constant is OPAQUE. It can however be unlocked to reveal the         *)
(* transparent constant reducebig, to let Coq expand summation on an explicit *)
(* sequence with an explicit test.                                            *)
(*   The lemmas can be classified according to the operator being iterated:   *)
(*  1. Results independent of the operator: extensionality with respect to    *)
(*     the range of indices, to the filtering predicate or to the expression  *)
(*     being iterated; reindexing, widening or narrowing of the range of      *)
(*     indices; we provide lemmas for the special cases where indices are     *)
(*     natural numbers or bounded natural numbers ("ordinals"). We supply     *)
(*     several "functional" induction principles that can be used with the    *)
(*     ssreflect 1.3 "elim" tactic to do induction over the index range for   *)
(*     up to 3 bigops simultaneously.                                         *)
(*  2. Results depending on the properties of the operator:                   *)
(*     We distinguish: monoid laws (op is associative, idx is an identity     *)
(*     element), abelian monoid laws (op is also commutative), and laws with  *)
(*     a distributive operation (semi-rings). Examples of such results are    *)
(*     splitting, permuting, and exchanging bigops.                           *)
(* A special section is dedicated to big operators on natural numbers.        *)
(******************************************************************************)
(* Notations:                                                                 *)
(* The general form for iterated operators is                                 *)
(*         <bigop>_<range> <general_term>                                     *)
(* - <bigop> is one of \big[op/idx], \sum, \prod, or \max (see below).        *)
(* - <general_term> can be any expression.                                    *)
(* - <range> binds an index variable in <general_term>; <range> is one of     *)
(*    (i <- s)     i ranges over the sequence s.                              *)
(*    (m <= i < n) i ranges over the nat interval m, m+1, ..., n-1.           *)
(*    (i < n)      i ranges over the (finite) type 'I_n (i.e., ordinal n).    *)
(*    (i : T)      i ranges over the finite type T.                           *)
(*    i or (i)     i ranges over its (inferred) finite type.                  *)
(*    (i in A)     i ranges over the elements that satisfy the collective     *)
(*                 predicate A (the domain of A must be a finite type).       *)
(*    (i <- s | <condition>) limits the range to the i for which <condition>  *)
(*                 holds. <condition> can be any expression that coerces to   *)
(*                 bool, and may mention the bound index i. All six kinds of  *)
(*                 ranges above can have a <condition> part.                  *)
(* - One can use the "\big[op/idx]" notations for any operator.               *)
(* - BIG_F and BIG_P are pattern abbreviations for the <general_term> and     *)
(*   <condition> part of a \big ... expression; for (i in A) and (i in A | C) *)
(*   ranges the term matched by BIG_P will include the i \in A condition.     *)
(* - The (locked) head constant of a \big notation is bigop.                  *)
(* - The "\sum", "\prod" and "\max" notations in the %N scope are used for    *)
(*   natural numbers with addition, multiplication and maximum (and their     *)
(*   corresponding neutral elements), respectively.                           *)
(* - The "\sum" and "\prod" reserved notations are overloaded in ssralg in    *)
(*   the %R scope; in mxalgebra, vector & falgebra in the %MS and %VS scopes; *)
(*   "\prod" is also overloaded in fingroup, in the %g and %G scopes.         *)
(* - We reserve "\bigcup" and "\bigcap" notations for iterated union and      *)
(*   intersection (of sets, groups, vector spaces, etc).                      *)
(******************************************************************************)
(* Tips for using lemmas in this file:                                        *)
(* To apply a lemma for a specific operator: if no special property is        *)
(* required for the operator, simply apply the lemma; if the lemma needs      *)
(* certain properties for the operator, make sure the appropriate Canonical   *)
(* instances are declared.                                                    *)
(******************************************************************************)
(* Interfaces for operator properties are packaged in the Monoid submodule:   *)
(*     Monoid.law idx == interface (keyed on the operator) for associative    *)
(*                       operators with identity element idx.                 *)
(* Monoid.com_law idx == extension (telescope) of Monoid.law for operators    *)
(*                       that are also commutative.                           *)
(* Monoid.mul_law abz == interface for operators with absorbing (zero)        *)
(*                       element abz.                                         *)
(* Monoid.add_law idx mop == extension of Monoid.com_law for operators over   *)
(*                       which operation mop distributes (mop will often also *)
(*                       have a Monoid.mul_law idx structure).                *)
(* [law of op], [com_law of op], [mul_law of op], [add_law mop of op] ==      *)
(*                       syntax for cloning Monoid structures.                *)
(*      Monoid.Theory == submodule containing basic generic algebra lemmas    *)
(*                       for operators satisfying the Monoid interfaces.      *)
(*       Monoid.simpm == generic monoid simplification rewrite multirule.     *)
(* Monoid structures are predeclared for many basic operators: (_ && _)%B,    *)
(* (_ || _)%B, (_ (+) _)%B (exclusive or) , (_ + _)%N, (_ * _)%N, maxn,       *)
(* gcdn, lcmn and (_ ++ _)%SEQ (list concatenation).                          *)
(******************************************************************************)
(* Additional documentation for this file:                                    *)
(* Y. Bertot, G. Gonthier, S. Ould Biha and I. Pasca.                         *)
(* Canonical Big Operators. In TPHOLs 2008, LNCS vol. 5170, Springer.         *)
(* Article available at:                                                      *)
(*     http://hal.inria.fr/docs/00/33/11/93/PDF/main.pdf                      *)
(******************************************************************************)
(* Examples of use in: poly.v, matrix.v                                       *)
(******************************************************************************)
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Reserved Notation "\big [ op / idx ]_ i F"
(at level 36, F at level 36, op, idx at level 10, i at level 0,
right associativity,
format "'[' \big [ op / idx ]_ i '/  '  F ']'").
Reserved Notation "\big [ op / idx ]_ ( i <- r | P ) F"
(at level 36, F at level 36, op, idx at level 10, i, r at level 50,
format "'[' \big [ op / idx ]_ ( i  <-  r  |  P ) '/  '  F ']'").
Reserved Notation "\big [ op / idx ]_ ( i <- r ) F"
(at level 36, F at level 36, op, idx at level 10, i, r at level 50,
format "'[' \big [ op / idx ]_ ( i  <-  r ) '/  '  F ']'").
Reserved Notation "\big [ op / idx ]_ ( m <= i < n | P ) F"
(at level 36, F at level 36, op, idx at level 10, m, i, n at level 50,
format "'[' \big [ op / idx ]_ ( m  <=  i  <  n  |  P )  F ']'").
Reserved Notation "\big [ op / idx ]_ ( m <= i < n ) F"
(at level 36, F at level 36, op, idx at level 10, i, m, n at level 50,
format "'[' \big [ op / idx ]_ ( m  <=  i  <  n ) '/  '  F ']'").
Reserved Notation "\big [ op / idx ]_ ( i | P ) F"
(at level 36, F at level 36, op, idx at level 10, i at level 50,
format "'[' \big [ op / idx ]_ ( i  |  P ) '/  '  F ']'").
Reserved Notation "\big [ op / idx ]_ ( i : t | P ) F"
(at level 36, F at level 36, op, idx at level 10, i at level 50,
format "'[' \big [ op / idx ]_ ( i   :  t   |  P ) '/  '  F ']'").
Reserved Notation "\big [ op / idx ]_ ( i : t ) F"
(at level 36, F at level 36, op, idx at level 10, i at level 50,
format "'[' \big [ op / idx ]_ ( i   :  t ) '/  '  F ']'").
Reserved Notation "\big [ op / idx ]_ ( i < n | P ) F"
(at level 36, F at level 36, op, idx at level 10, i, n at level 50,
format "'[' \big [ op / idx ]_ ( i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\big [ op / idx ]_ ( i < n ) F"
(at level 36, F at level 36, op, idx at level 10, i, n at level 50,
format "'[' \big [ op / idx ]_ ( i  <  n )  F ']'").
Reserved Notation "\big [ op / idx ]_ ( i 'in' A | P ) F"
(at level 36, F at level 36, op, idx at level 10, i, A at level 50,
format "'[' \big [ op / idx ]_ ( i  'in'  A  |  P ) '/  '  F ']'").
Reserved Notation "\big [ op / idx ]_ ( i 'in' A ) F"
(at level 36, F at level 36, op, idx at level 10, i, A at level 50,
format "'[' \big [ op / idx ]_ ( i  'in'  A ) '/  '  F ']'").
Reserved Notation "\sum_ i F"
(at level 41, F at level 41, i at level 0,
right associativity,
format "'[' \sum_ i '/  '  F ']'").
Reserved Notation "\sum_ ( i <- r | P ) F"
(at level 41, F at level 41, i, r at level 50,
format "'[' \sum_ ( i  <-  r  |  P ) '/  '  F ']'").
Reserved Notation "\sum_ ( i <- r ) F"
(at level 41, F at level 41, i, r at level 50,
format "'[' \sum_ ( i  <-  r ) '/  '  F ']'").
Reserved Notation "\sum_ ( m <= i < n | P ) F"
(at level 41, F at level 41, i, m, n at level 50,
format "'[' \sum_ ( m  <=  i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\sum_ ( m <= i < n ) F"
(at level 41, F at level 41, i, m, n at level 50,
format "'[' \sum_ ( m  <=  i  <  n ) '/  '  F ']'").
Reserved Notation "\sum_ ( i | P ) F"
(at level 41, F at level 41, i at level 50,
format "'[' \sum_ ( i  |  P ) '/  '  F ']'").
Reserved Notation "\sum_ ( i : t | P ) F"
(at level 41, F at level 41, i at level 50,
only parsing).
Reserved Notation "\sum_ ( i : t ) F"
(at level 41, F at level 41, i at level 50,
only parsing).
Reserved Notation "\sum_ ( i < n | P ) F"
(at level 41, F at level 41, i, n at level 50,
format "'[' \sum_ ( i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\sum_ ( i < n ) F"
(at level 41, F at level 41, i, n at level 50,
format "'[' \sum_ ( i  <  n ) '/  '  F ']'").
Reserved Notation "\sum_ ( i 'in' A | P ) F"
(at level 41, F at level 41, i, A at level 50,
format "'[' \sum_ ( i  'in'  A  |  P ) '/  '  F ']'").
Reserved Notation "\sum_ ( i 'in' A ) F"
(at level 41, F at level 41, i, A at level 50,
format "'[' \sum_ ( i  'in'  A ) '/  '  F ']'").
Reserved Notation "\max_ i F"
(at level 41, F at level 41, i at level 0,
format "'[' \max_ i '/  '  F ']'").
Reserved Notation "\max_ ( i <- r | P ) F"
(at level 41, F at level 41, i, r at level 50,
format "'[' \max_ ( i  <-  r  |  P ) '/  '  F ']'").
Reserved Notation "\max_ ( i <- r ) F"
(at level 41, F at level 41, i, r at level 50,
format "'[' \max_ ( i  <-  r ) '/  '  F ']'").
Reserved Notation "\max_ ( m <= i < n | P ) F"
(at level 41, F at level 41, i, m, n at level 50,
format "'[' \max_ ( m  <=  i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\max_ ( m <= i < n ) F"
(at level 41, F at level 41, i, m, n at level 50,
format "'[' \max_ ( m  <=  i  <  n ) '/  '  F ']'").
Reserved Notation "\max_ ( i | P ) F"
(at level 41, F at level 41, i at level 50,
format "'[' \max_ ( i  |  P ) '/  '  F ']'").
Reserved Notation "\max_ ( i : t | P ) F"
(at level 41, F at level 41, i at level 50,
only parsing).
Reserved Notation "\max_ ( i : t ) F"
(at level 41, F at level 41, i at level 50,
only parsing).
Reserved Notation "\max_ ( i < n | P ) F"
(at level 41, F at level 41, i, n at level 50,
format "'[' \max_ ( i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\max_ ( i < n ) F"
(at level 41, F at level 41, i, n at level 50,
format "'[' \max_ ( i  <  n )  F ']'").
Reserved Notation "\max_ ( i 'in' A | P ) F"
(at level 41, F at level 41, i, A at level 50,
format "'[' \max_ ( i  'in'  A  |  P ) '/  '  F ']'").
Reserved Notation "\max_ ( i 'in' A ) F"
(at level 41, F at level 41, i, A at level 50,
format "'[' \max_ ( i  'in'  A ) '/  '  F ']'").
Reserved Notation "\prod_ i F"
(at level 36, F at level 36, i at level 0,
format "'[' \prod_ i '/  '  F ']'").
Reserved Notation "\prod_ ( i <- r | P ) F"
(at level 36, F at level 36, i, r at level 50,
format "'[' \prod_ ( i  <-  r  |  P ) '/  '  F ']'").
Reserved Notation "\prod_ ( i <- r ) F"
(at level 36, F at level 36, i, r at level 50,
format "'[' \prod_ ( i  <-  r ) '/  '  F ']'").
Reserved Notation "\prod_ ( m <= i < n | P ) F"
(at level 36, F at level 36, i, m, n at level 50,
format "'[' \prod_ ( m  <=  i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\prod_ ( m <= i < n ) F"
(at level 36, F at level 36, i, m, n at level 50,
format "'[' \prod_ ( m  <=  i  <  n ) '/  '  F ']'").
Reserved Notation "\prod_ ( i | P ) F"
(at level 36, F at level 36, i at level 50,
format "'[' \prod_ ( i  |  P ) '/  '  F ']'").
Reserved Notation "\prod_ ( i : t | P ) F"
(at level 36, F at level 36, i at level 50,
only parsing).
Reserved Notation "\prod_ ( i : t ) F"
(at level 36, F at level 36, i at level 50,
only parsing).
Reserved Notation "\prod_ ( i < n | P ) F"
(at level 36, F at level 36, i, n at level 50,
format "'[' \prod_ ( i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\prod_ ( i < n ) F"
(at level 36, F at level 36, i, n at level 50,
format "'[' \prod_ ( i  <  n ) '/  '  F ']'").
Reserved Notation "\prod_ ( i 'in' A | P ) F"
(at level 36, F at level 36, i, A at level 50,
format "'[' \prod_ ( i  'in'  A  |  P )  F ']'").
Reserved Notation "\prod_ ( i 'in' A ) F"
(at level 36, F at level 36, i, A at level 50,
format "'[' \prod_ ( i  'in'  A ) '/  '  F ']'").
Reserved Notation "\bigcup_ i F"
(at level 41, F at level 41, i at level 0,
format "'[' \bigcup_ i '/  '  F ']'").
Reserved Notation "\bigcup_ ( i <- r | P ) F"
(at level 41, F at level 41, i, r at level 50,
format "'[' \bigcup_ ( i  <-  r  |  P ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( i <- r ) F"
(at level 41, F at level 41, i, r at level 50,
format "'[' \bigcup_ ( i  <-  r ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( m <= i < n | P ) F"
(at level 41, F at level 41, m, i, n at level 50,
format "'[' \bigcup_ ( m  <=  i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( m <= i < n ) F"
(at level 41, F at level 41, i, m, n at level 50,
format "'[' \bigcup_ ( m  <=  i  <  n ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( i | P ) F"
(at level 41, F at level 41, i at level 50,
format "'[' \bigcup_ ( i  |  P ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( i : t | P ) F"
(at level 41, F at level 41, i at level 50,
format "'[' \bigcup_ ( i   :  t   |  P ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( i : t ) F"
(at level 41, F at level 41, i at level 50,
format "'[' \bigcup_ ( i   :  t ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( i < n | P ) F"
(at level 41, F at level 41, i, n at level 50,
format "'[' \bigcup_ ( i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( i < n ) F"
(at level 41, F at level 41, i, n at level 50,
format "'[' \bigcup_ ( i  <  n ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( i 'in' A | P ) F"
(at level 41, F at level 41, i, A at level 50,
format "'[' \bigcup_ ( i  'in'  A  |  P ) '/  '  F ']'").
Reserved Notation "\bigcup_ ( i 'in' A ) F"
(at level 41, F at level 41, i, A at level 50,
format "'[' \bigcup_ ( i  'in'  A ) '/  '  F ']'").
Reserved Notation "\bigcap_ i F"
(at level 41, F at level 41, i at level 0,
format "'[' \bigcap_ i '/  '  F ']'").
Reserved Notation "\bigcap_ ( i <- r | P ) F"
(at level 41, F at level 41, i, r at level 50,
format "'[' \bigcap_ ( i  <-  r  |  P )  F ']'").
Reserved Notation "\bigcap_ ( i <- r ) F"
(at level 41, F at level 41, i, r at level 50,
format "'[' \bigcap_ ( i  <-  r ) '/  '  F ']'").
Reserved Notation "\bigcap_ ( m <= i < n | P ) F"
(at level 41, F at level 41, m, i, n at level 50,
format "'[' \bigcap_ ( m  <=  i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\bigcap_ ( m <= i < n ) F"
(at level 41, F at level 41, i, m, n at level 50,
format "'[' \bigcap_ ( m  <=  i  <  n ) '/  '  F ']'").
Reserved Notation "\bigcap_ ( i | P ) F"
(at level 41, F at level 41, i at level 50,
format "'[' \bigcap_ ( i  |  P ) '/  '  F ']'").
Reserved Notation "\bigcap_ ( i : t | P ) F"
(at level 41, F at level 41, i at level 50,
format "'[' \bigcap_ ( i   :  t   |  P ) '/  '  F ']'").
Reserved Notation "\bigcap_ ( i : t ) F"
(at level 41, F at level 41, i at level 50,
format "'[' \bigcap_ ( i   :  t ) '/  '  F ']'").
Reserved Notation "\bigcap_ ( i < n | P ) F"
(at level 41, F at level 41, i, n at level 50,
format "'[' \bigcap_ ( i  <  n  |  P ) '/  '  F ']'").
Reserved Notation "\bigcap_ ( i < n ) F"
(at level 41, F at level 41, i, n at level 50,
format "'[' \bigcap_ ( i  <  n ) '/  '  F ']'").
Reserved Notation "\bigcap_ ( i 'in' A | P ) F"
(at level 41, F at level 41, i, A at level 50,
format "'[' \bigcap_ ( i  'in'  A  |  P ) '/  '  F ']'").
Reserved Notation "\bigcap_ ( i 'in' A ) F"
(at level 41, F at level 41, i, A at level 50,
format "'[' \bigcap_ ( i  'in'  A ) '/  '  F ']'").
Module Monoid.
Section Definitions.
Variables (T : Type) (idm : T).
Structure law := Law {
operator : T -> T -> T;
_ : associative operator;
_ : left_id idm operator;
_ : right_id idm operator
}.
Local Coercion operator : law >-> Funclass.
Structure com_law := ComLaw {
com_operator : law;
_ : commutative com_operator
}.
Local Coercion com_operator : com_law >-> law.
Structure mul_law := MulLaw {
mul_operator : T -> T -> T;
_ : left_zero idm mul_operator;
_ : right_zero idm mul_operator
}.
Local Coercion mul_operator : mul_law >-> Funclass.
Structure add_law (mul : T -> T -> T) := AddLaw {
add_operator : com_law;
_ : left_distributive mul add_operator;
_ : right_distributive mul add_operator
}.
Local Coercion add_operator : add_law >-> com_law.
Let op_id (op1 op2 : T -> T -> T) := phant_id op1 op2.
Definition clone_law op :=
fun (opL : law) & op_id opL op =>
fun opmA op1m opm1 (opL' := @Law op opmA op1m opm1)
& phant_id opL' opL => opL'.
Definition clone_com_law op :=
fun (opL : law) (opC : com_law) & op_id opL op & op_id opC op =>
fun opmC (opC' := @ComLaw opL opmC) & phant_id opC' opC => opC'.
Definition clone_mul_law op :=
fun (opM : mul_law) & op_id opM op =>
fun op0m opm0 (opM' := @MulLaw op op0m opm0) & phant_id opM' opM => opM'.
Definition clone_add_law mop aop :=
fun (opC : com_law) (opA : add_law mop) & op_id opC aop & op_id opA aop =>
fun mopDm mopmD (opA' := @AddLaw mop opC mopDm mopmD)
& phant_id opA' opA => opA'.
End Definitions.
Module Import Exports.
Coercion operator : law >-> Funclass.
Coercion com_operator : com_law >-> law.
Coercion mul_operator : mul_law >-> Funclass.
Coercion add_operator : add_law >-> com_law.
Notation "[ 'law' 'of' f ]" := (@clone_law _ _ f _ id _ _ _ id)
(at level 0, format"[ 'law'  'of'  f ]") : form_scope.
Notation "[ 'com_law' 'of' f ]" := (@clone_com_law _ _ f _ _ id id _ id)
(at level 0, format "[ 'com_law'  'of'  f ]") : form_scope.
Notation "[ 'mul_law' 'of' f ]" := (@clone_mul_law _ _ f _ id _ _ id)
(at level 0, format"[ 'mul_law'  'of'  f ]") : form_scope.
Notation "[ 'add_law' m 'of' a ]" := (@clone_add_law _ _ m a _ _ id id _ _ id)
(at level 0, format "[ 'add_law'  m  'of'  a ]") : form_scope.
End Exports.
Section CommutativeAxioms.
Variable (T : Type) (zero one : T) (mul add : T -> T -> T) (inv : T -> T).
Hypothesis mulC : commutative mul.

Lemma mem_index_enum T i : i \in index_enum T.
Hypotheses (fM : {morph f : x y / op x y}) (f_id : f idx = idx).