(**
  II. Defines term maps which allow us to
    interpret abstract terms as monoid values.
*)

(**
  Represents a mapping from abstract terms
  to monoid set elements.
*)
Structure Term_map : Type := term_map {
  (**
    Represents the monoid set that terms will be 
    projected onto. 
  *)
  term_map_m: Monoid;

  (**
    Represents the set of terms that will be
    used to represent monoid values.
  *)
  term_map_term : Set;

  (**
    Accepts a term and returns its projection
    in E.
  *)
  term_map_eval : term_map_term -> E term_map_m;

  (**
    Accepts a term and returns true iff the term
    represents the monoid identity element (0).
  *)
  term_map_is_zero : term_map_term -> bool;

  (**
    Accepts a term and proves that zero terms
    evaluate to 0.
  *)
  term_map_is_zero_thm : forall t, term_map_is_zero t = true -> term_map_eval t = 0
}.(**
  II. Defines term maps which allow us to
    interpret abstract terms as monoid values.
*)

(**
  Represents a mapping from abstract terms
  to monoid set elements.
*)
Structure Term_map : Type := term_map {
  (**
    Represents the monoid set that terms will be 
    projected onto. 
  *)
  term_map_m: Monoid;

  (**
    Represents the set of terms that will be
    used to represent monoid values.
  *)
  term_map_term : Set;

  (**
    Accepts a term and returns its projection
    in E.
  *)
  term_map_eval : term_map_term -> E term_map_m;

  (**
    Accepts a term and returns true iff the term
    represents the monoid identity element (0).
  *)
  term_map_is_zero : term_map_term -> bool;

  (**
    Accepts a term and proves that zero terms
    evaluate to 0.
  *)
  term_map_is_zero_thm : forall t, term_map_is_zero t = true -> term_map_eval t = 0
}.(**
  II. Defines term maps which allow us to
    interpret abstract terms as monoid values.
*)

(**
  Represents a mapping from abstract terms
  to monoid set elements.
*)
Structure Term_map : Type := term_map {
  (**
    Represents the monoid set that terms will be 
    projected onto. 
  *)
  term_map_m: Monoid;

  (**
    Represents the set of terms that will be
    used to represent monoid values.
  *)
  term_map_term : Set;

  (**
    Accepts a term and returns its projection
    in E.
  *)
  term_map_eval : term_map_term -> E term_map_m;

  (**
    Accepts a term and returns true iff the term
    represents the monoid identity element (0).
  *)
  term_map_is_zero : term_map_term -> bool;

  (**
    Accepts a term and proves that zero terms
    evaluate to 0.
  *)
  term_map_is_zero_thm : forall t, term_map_is_zero t = true -> term_map_eval t = 0
}.(**
  II. Defines term maps which allow us to
    interpret abstract terms as monoid values.
*)

(**
  Represents a mapping from abstract terms
  to monoid set elements.
*)
Structure Term_map : Type := term_map {
  (**
    Represents the monoid set that terms will be 
    projected onto. 
  *)
  term_map_m: Monoid;

  (**
    Represents the set of terms that will be
    used to represent monoid values.
  *)
  term_map_term : Set;

  (**
    Accepts a term and returns its projection
    in E.
  *)
  term_map_eval : term_map_term -> E term_map_m;

  (**
    Accepts a term and returns true iff the term
    represents the monoid identity element (0).
  *)
  term_map_is_zero : term_map_term -> bool;

  (**
    Accepts a term and proves that zero terms
    evaluate to 0.
  *)
  term_map_is_zero_thm : forall t, term_map_is_zero t = true -> term_map_eval t = 0
}.(**
  II. Defines term maps which allow us to
    interpret abstract terms as monoid values.
*)

(**
  Represents a mapping from abstract terms
  to monoid set elements.
*)
Structure Term_map : Type := term_map {
  (**
    Represents the monoid set that terms will be 
    projected onto. 
  *)
  term_map_m: Monoid;

  (**
    Represents the set of terms that will be
    used to represent monoid values.
  *)
  term_map_term : Set;

  (**
    Accepts a term and returns its projection
    in E.
  *)
  term_map_eval : term_map_term -> E term_map_m;

  (**
    Accepts a term and returns true iff the term
    represents the monoid identity element (0).
  *)
  term_map_is_zero : term_map_term -> bool;

  (**
    Accepts a term and proves that zero terms
    evaluate to 0.
  *)
  term_map_is_zero_thm : forall t, term_map_is_zero t = true -> term_map_eval t = 0
}.(**
  II. Defines term maps which allow us to
    interpret abstract terms as monoid values.
*)

(**
  Represents a mapping from abstract terms
  to monoid set elements.
*)
Structure Term_map : Type := term_map {
  (**
    Represents the monoid set that terms will be 
    projected onto. 
  *)
  term_map_m: Monoid;

  (**
    Represents the set of terms that will be
    used to represent monoid values.
  *)
  term_map_term : Set;

  (**
    Accepts a term and returns its projection
    in E.
  *)
  term_map_eval : term_map_term -> E term_map_m;

  (**
    Accepts a term and returns true iff the term
    represents the monoid identity element (0).
  *)
  term_map_is_zero : term_map_term -> bool;

  (**
    Accepts a term and proves that zero terms
    evaluate to 0.
  *)
  term_map_is_zero_thm : forall t, term_map_is_zero t = true -> term_map_eval t = 0
}.(**
  II. Defines term maps which allow us to
    interpret abstract terms as monoid values.
*)

(**
  Represents a mapping from abstract terms
  to monoid set elements.
*)
Structure Term_map : Type := term_map {
  (**
    Represents the monoid set that terms will be 
    projected onto. 
  *)
  term_map_m: Monoid;

  (**
    Represents the set of terms that will be
    used to represent monoid values.
  *)
  term_map_term : Set;

  (**
    Accepts a term and returns its projection
    in E.
  *)
  term_map_eval : term_map_term -> E term_map_m;

  (**
    Accepts a term and returns true iff the term
    represents the monoid identity element (0).
  *)
  term_map_is_zero : term_map_term -> bool;

  (**
    Accepts a term and proves that zero terms
    evaluate to 0.
  *)
  term_map_is_zero_thm : forall t, term_map_is_zero t = true -> term_map_eval t = 0
}.(**
  Accepts a term and returns true iff it is not
  a zero constant term.
*)
Definition Term_is_nonzero
  :  Term -> bool
  := fun t => negb (term_map_is_zero t).(** Maps binary trees onto monoid expressions. *)
Definition BTree_eval
  :  BTree Term -> E
  := BTree_rec Term
       (fun _ => E)
       (fun t => term_map_eval t)
       (fun _ f _ g => f + g).(**
  Accepts two monoid expressions and returns
  true iff they are denotationally equivalent -
  I.E. represent the same monoid value.
*)
Definition BTree_eq
  :  BTree Term -> BTree Term -> Prop
  := fun t u => BTree_eval t = BTree_eval u.(**
  IV. Defines functions for evaluating and
    transforming lists of terms that can be mapped
    onto monoid values using a term map.


  In the following section, we use the
  isomorphism between right associative binary
  trees and lists to represent monoid expressions
  as lists and to use list filtering to eleminate
  identity elements. This is part of a larger
  effort to "simplify" momoid expressions.
*)

(**
  Accepts a list of monoid elements and computes
  their sum.
*)
Definition list_eval
  :  forall xs : list Term, E
  := list_rec
       (fun _ => E)
       0
       (fun x _ f => (term_map_eval x) + f).(**
  Accepts two term lists and asserts that they
  are equivalent.
*)
Definition list_eq : list Term -> list Term -> Prop
  := fun xs ys : list Term
       => list_eval xs = list_eval ys.(**
  Represents monoid values.

  Note: In the development that follows, we
  will use binary trees and lists to represent
  monoid expressions. We will effectively flatten
  a tree and filter a list to "simplify"
  a given expression.

  The code that flattens the tree representation
  does not need to care whether or not the
  leaves in the tree represent 0 (the monoid
  identity element), inverses, etc. Accordingly,
  distinguishing these elements in the definition
  of BTree would unnecessarily complicate the
  tree algorithms by adding more recursion cases.

  Instead of doing this, we use two types
  to represent monoid expressions - trees to
  represent "terms" (expressions that are summed
  together) and Term. Term tracks whether or
  not a monoid value equals 0 (and later we will
  use a similar structure to indicate whether or
  not a given group element is an inverse). This
  makes this information available when needed
  (specifically when we eliminate 0s using
  list filtering) without complicating the
  tree algorithms.
*)
Inductive Term : Set
  := term_0 : Term
  |  term_const : E -> Term.(**
  Represents monoid values.

  Note: In the development that follows, we
  will use binary trees and lists to represent
  monoid expressions. We will effectively flatten
  a tree and filter a list to "simplify"
  a given expression.

  The code that flattens the tree representation
  does not need to care whether or not the
  leaves in the tree represent 0 (the monoid
  identity element), inverses, etc. Accordingly,
  distinguishing these elements in the definition
  of BTree would unnecessarily complicate the
  tree algorithms by adding more recursion cases.

  Instead of doing this, we use two types
  to represent monoid expressions - trees to
  represent "terms" (expressions that are summed
  together) and Term. Term tracks whether or
  not a monoid value equals 0 (and later we will
  use a similar structure to indicate whether or
  not a given group element is an inverse). This
  makes this information available when needed
  (specifically when we eliminate 0s using
  list filtering) without complicating the
  tree algorithms.
*)
Inductive Term : Set
  := term_0 : Term
  |  term_const : E -> Term.(**
  Represents monoid values.

  Note: In the development that follows, we
  will use binary trees and lists to represent
  monoid expressions. We will effectively flatten
  a tree and filter a list to "simplify"
  a given expression.

  The code that flattens the tree representation
  does not need to care whether or not the
  leaves in the tree represent 0 (the monoid
  identity element), inverses, etc. Accordingly,
  distinguishing these elements in the definition
  of BTree would unnecessarily complicate the
  tree algorithms by adding more recursion cases.

  Instead of doing this, we use two types
  to represent monoid expressions - trees to
  represent "terms" (expressions that are summed
  together) and Term. Term tracks whether or
  not a monoid value equals 0 (and later we will
  use a similar structure to indicate whether or
  not a given group element is an inverse). This
  makes this information available when needed
  (specifically when we eliminate 0s using
  list filtering) without complicating the
  tree algorithms.
*)
Inductive Term : Set
  := term_0 : Term
  |  term_const : E -> Term.(**
  Accepts a term and returns the monoid value
  that it represents.
*)
Definition Term_eval
  :  Term -> E
  := Term_rec
       (fun _ => E)
       0
       (fun x => x).(**
  Accepts a term and returns true iff the term
  is zero.
*)
Definition Term_is_zero
  :  Term -> bool
  := Term_rec
       (fun _ => bool)
       true
       (fun _ => false).

Theorem Term_is_zero_thm : forall t, Term_is_zero t = true -> Term_eval t = 0.