(***************************************************************************)
(*  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). Additionnaly, some A or AC operators can have units (U).

    This module could be reused outside of the Coq plugin.

    Matching a pattern [p] against a term [t] modulo AACU boils down
    to finding a substitution [env] such that the pattern [p]
    instantiated with [env] is equal to [t] modulo AACU.

    We proceed by structural decomposition of the pattern, trying all
    possible non-deterministic splittings 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_monad} 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 AACU. In particular, this function
    gives a solution to the aforementioned case ([x+x] against
    [a+b+a]).

    On a slightly more involved level :
    - it must be noted that we allow several AC/A operators to share
    the same units, but that a given AC/A operator can have at most
    one unit.

    - if the pattern does not contain "hard" symbols (like constants,
    function symbols, AC or A symbols without units), there can be
    infinitely many subterms such that the pattern matches: it is
    possible to build "subterms" modulo AAC and U that make the size
    of the term increase (by making neutral elements appear in a
    layered fashion). Hence, in this case, a warning is issued, and
    some solutions are omitted.
   
*)

(** {2 Utility functions}  *)

type symbol = int
type var = int

(** Relationship between units and operators. This is a sparse
    representation of a matrix of couples [(op,unit)] where [op] is
    the index of the operation, and [unit] the index of the relevant
    unit. We make the assumption that any operation has 0 or 1 unit,
    and that operations can share a unit). *)

type units =(symbol * symbol) list (* from AC/A symbols to the unit *)
type ext_units =
    {
      unit_for : units; (* from AC/A symbols to the unit *)     
      is_ac : (symbol * bool) list
    } 	


(** 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 AACU. 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, Unit (1); Dot(4, Var x,
      Sym(5,[|Sym(3,[||])|])) |])] represents the pattern [forall x, f
      (x^1) (x*g b)]. The relationship between [1] and [( * )] is only
      mentionned in the units table.  *)

  type t =
      Dot of (symbol * t * t)
    | Plus of (symbol * t * t)
    | Sym of (symbol * t array)
    | Var of var
    | Unit of symbol

  (** Test for equality of terms modulo AACU (relies on the following
      canonical representation of terms).  Note than two different
      units of a same operator are not considered equal.  *)
  val equal_aac : units ->  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 AACU.
      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 AACU
      iif [nf_equal (term_of_t a) (term_of_t b) = true]   *)
  val term_of_t : units -> 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 -> ext_units -> Terms.t -> Terms.t -> Subst.t Search_monad.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 -> ext_units -> Terms.t -> Terms.t -> (int * Terms.t * Subst.t Search_monad.m) Search_monad.m

