; Fully Ordered Finite Sets, Version 0.9
; Copyright (C) 2003, 2004 Kookamara LLC
;
; Contact:
;
;   Kookamara LLC
;   11410 Windermere Meadows
;   Austin, TX 78759, USA
;   http://www.kookamara.com/
;
; License: (An MIT/X11-style license)
;
;   Permission is hereby granted, free of charge, to any person obtaining a
;   copy of this software and associated documentation files (the "Software"),
;   to deal in the Software without restriction, including without limitation
;   the rights to use, copy, modify, merge, publish, distribute, sublicense,
;   and/or sell copies of the Software, and to permit persons to whom the
;   Software is furnished to do so, subject to the following conditions:
;
;   The above copyright notice and this permission notice shall be included in
;   all copies or substantial portions of the Software.
;
;   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;   DEALINGS IN THE SOFTWARE.
;
; Original author: Jared Davis <jared@kookamara.com>
;
; instance.lisp
;
; This is a system for dynamically instantiating ACL2 "theories" (which are
; represented as constants) to create new, concrete "theories".

(in-package "INSTANCE")


; Everything in this file is in program mode.  We do not intend to 
; reason about these functions -- instead, we intend to use these
; functions to create new functions, which the user will reason 
; about.

(program)


; Introduction
;
;
; The following work has been motivated by my work with quantification
; over sets.  When I started on this file, I had roughly 2000 lines of
; complicted macros in order to be able to instantiate generic and
; concrete theories for this work, and it was just becoming
; unmanageable.  My hope was that rewriting the definitions and
; theorems into concrete forms would provide a more concise way of
; instantiating the theory, and make it easier to keep everything
; consistent.
;
; Originally, I wanted to extract the definitions for generic
; functions from ACL2's state (well, actually from the current world).
; But, to do so becomes very complicated, because of the restrictions
; on macros that they cannot take state as a parameter.  So, the best
; that I could ever accomplish that way would be to display a list of
; events, which a user could copy into a file.  But, that is wholly
; unsatisfying, because it would mean that the resulting theories
; could never be "automagically" updated when new theorems are added
; to the generic theory.
;
; So, instead of doing things that way, I now simply store events in 
; constants.  These constants can then be rewritten to create new but
; related theories.
;
; A first step towards this is to introduce a simple rewriter.
; Originally I had based my rewriter on the built in one-way-unify
; function in ACL2, but it operates only on pseudo-terms, and
; pseudo-terms cannot contain atoms other than symbols.  This gave me
; serious trouble when trying to rewrite theorems involving constants,
; e.g., to say that something was an integerp and greater than zero.
; So, instead of using one-way-unify, I introduce a simple unification
; algorithm which has been adapted from Warren Hunt's work.  

; The system treats all symbols beginning with a ? as variables, and
; all other atoms as literals.

(defun instance-variablep (x)
  (and (symbolp x)
       (equal (car (explode-atom x 10)) #\?)))



; We return two values: a boolean flag which indicates if we are
; successful in finding a match, and a list of substitutions of the
; form (variable . value).  This is all be fairly standard stuff.
; 
; For example:
;
;    (instance-unify-term '(predicate ?x) '(predicate (car a)) nil)
;    ==>
;    (t ((?x . (car a))))

(mutual-recursion 

  (defun instance-unify-term (pattern term sublist)
    (if (atom pattern)
	(if (instance-variablep pattern)
	    (let ((value (assoc pattern sublist)))
	      (if (consp value)
		  (if (equal term (cdr value))
		      (mv t sublist)
		    (mv nil nil))
		(mv t (acons pattern term sublist))))
	  (if (equal term pattern)
	      (mv t sublist)
	    (mv nil nil)))
      (if (or (atom term)
	      (not (eq (car term) (car pattern))))
	  (mv nil nil)
	(if (or (eq (car term) 'quote)
		(eq (car pattern) 'quote))
	    (if (equal term pattern)
		(mv t sublist)
	      (mv nil nil))
	  (instance-unify-list (cdr pattern) (cdr term) sublist)))))
      
  (defun instance-unify-list (pattern-list term-list sublist)
    (if (or (atom term-list)
	    (atom pattern-list))
	(if (and (atom term-list)
		 (atom pattern-list))
	    (mv t sublist)
	  (mv nil nil))
      (mv-let (successp new-sublist)
	      (instance-unify-term (car pattern-list) 
				   (car term-list) 
				   sublist)
	      (if successp
		  (instance-unify-list (cdr pattern-list) 
				       (cdr term-list) 
				       new-sublist)
		(mv nil nil)))))
)


; After a list of substitutions has been generated, we typically want
; to apply them to a term.  We recur over the list of substitutions, 
; simply calling subst to do our work throughout a term.
;
; For example:
; 
;   (instance-substitute '((?x . (car a))) '(not (predicate ?x)))
;   ==>
;   (not (predicate (car a)))

(defun instance-substitute (sublist term)
  (if (endp sublist)
      term
    (let* ((old (car (car sublist)))
           (new (cdr (car sublist)))
	   (result (subst new old term)))
      (instance-substitute (cdr sublist) result))))



; We now introduce our actual rewriter.  We take three arguments: pat
; is the pattern to look for throughout the term, e.g., (predicate
; ?x), repl is the replacement to use, e.g., (not (predicate ?x)), and
; term is the term to match the pattern against in order to get the
; substitutions.
;
; For Example:
;  
;   (instance-rewrite1 '(predicate ?x) 
;                      '(not (predicate ?x))
;                      '(if (predicate (car x)) t nil))
;   =>
;   (if (not (predicate (car x))) t nil)

(mutual-recursion 

  (defun instance-rewrite1 (pat repl term)
    (mv-let (successful sublist)
            (instance-unify-term pat term nil)
	    (if successful 
		(instance-substitute sublist repl)
	      (if (atom term)
		  term
		(cons (instance-rewrite1 pat repl (car term))
		      (instance-rewrite-lst1 pat repl (cdr term)))))))

  (defun instance-rewrite-lst1 (pat repl lst)
    (if (endp lst)
        nil
      (cons (instance-rewrite1 pat repl (car lst))
            (instance-rewrite-lst1 pat repl (cdr lst)))))
)



; Finally, given that we can apply a rewrite a term with a single
; replacement, we go ahead and extend this notion to multiple
; replacements.  In other words, we walk through a list of
; substitutions, sequentially rewriting the term using each
; substitution.

(defun instance-rewrite (term subs)
  (if (endp subs)
      term
    (let ((first-sub (car subs)))
      (instance-rewrite (instance-rewrite1 (first first-sub)
					   (second first-sub)
					   term)
			(cdr subs)))))




; Instantiating Defuns
;
;
; Theories consist mainly of definitions and theorems.  Given generic
; theorems, we will want to rewrite them so that they perform
; different functions.  For example, a generic "all" function might
; need to be rewritten so that its calls to (predicate x) are replaced
; with calls to (not (predicate x)) for all x.  
; 
; To begin, we instantiate the function's declarations (e.g., comment
; strings, xargs, ignores, and so forth).  We simply duplicate comment
; strings, but for declare forms we allow rewriting to occur.

(defun instance-decls (decls subs)
  (if (endp decls)
      nil
    (if (pseudo-termp (car decls))
	(cons (instance-rewrite (car decls) subs)
	      (instance-decls (cdr decls) subs))
      (cons (car decls)
	    (instance-decls (cdr decls) subs)))))


; For the defun itself, we retain the same defun symbol (e.g., defun
; or defund), but we change the name and args of the function by first
; creating the list '(oldname oldarg1 oldarg2 ...) then applying our
; substitutions to the new function.
;
; As a trivial example, 
;  (instance-defun '(defun f (x) (+ x 1)) '(((f x) (g x))))
;    =>
;  (defun g (x) (+ x 1))

(defun instance-defun (defun subs)
  (let* ((defun-symbol  (first defun))
 	 (defun-name    (second defun))
	 (defun-args    (third defun))
         (defun-decls   (butlast (cdddr defun) 1))
	 (defun-body    (car (last defun)))
	 (name/args     (cons defun-name defun-args))
	 (new-body      (instance-rewrite defun-body subs))
	 (new-name/args (instance-rewrite name/args subs))
	 (new-decls     (instance-decls defun-decls subs))
	 (new-name      (car new-name/args))
	 (new-args      (cdr new-name/args)))
    `(,defun-symbol 
       ,new-name ,new-args 
       ,@new-decls
       ,new-body)))

; We also provide a convenience function that allows you to instance
; a list of defuns.

(defun instance-defuns (defun-list subs)
  (if (endp defun-list)
      nil
    (cons (instance-defun (car defun-list) subs)
	  (instance-defuns (cdr defun-list) subs))))



; Renaming theorems

(defun defthm-names (event-list)
  (if (endp event-list)
      nil
    (let* ((first-event (car event-list))
	   (event-type  (first first-event)))
      (cond ((or (eq event-type 'defthm)
		 (eq event-type 'defthmd))
	     (cons (second first-event)
		   (defthm-names (cdr event-list))))
	    ((eq event-type 'encapsulate)
	     (append (defthm-names (cddr first-event))
		     (defthm-names (cdr event-list))))
	    (t (defthm-names (cdr event-list)))))))

(defun create-new-names (name-list suffix)
  (if (endp name-list)
      nil
    (acons (car name-list)
	   (intern-in-package-of-symbol (string-append (symbol-name (car name-list))
						       (symbol-name suffix))
					suffix)
	   (create-new-names (cdr name-list) suffix))))
	
(defun rename-defthms (event-list suffix)
  (sublis (create-new-names (defthm-names event-list) suffix)
	  event-list))



; Instantiating Theorems
;
;
; To instantiate defthms, we will want to be able to provide
; functional instantiations of the generic theory.  This is much
; more complicated than instancing definitions, and involves:
;
;   a) determining what functional substitutions to make
;   b) determining the theory in which to conduct the proofs 
;   c) handling rule classes and other optional components
;   d) generating the actual defthm event
;
; My idea is essentially that if a substitution list can be used for
; functionally instantiating theorems, then it can also be used for 
; creating the new theorem.
;
; (a) Determining what functional substitutions to make.
;
; I pass in a list of substitutions of the following form.
;
;   (((predicate ?x)  (not (in ?x y)))
;    ((all ?x)        (all-not-in ?x y))
;    ((exists ?x)     (exists-not-in ?x y)))
; 
; From this list we can generate the functional instantiation hints.
; So, for example, we simply convert ((predicate ?x) (not (in ?x y)))
; into the substitution:
;
;   (predicate (lambda (?x) (not (in ?x y))))
;
; This is easy to do with the following functions:

(defun sub-to-lambda (sub)
  (let ((term (first sub))
	(repl (second sub)))
    (let ((function-symbol (car term))
	  (lambda-args (cdr term)))
      `(,function-symbol (lambda ,lambda-args ,repl)))))

(defun subs-to-lambdas (subs)
  (if (endp subs)
      nil
    (cons (sub-to-lambda (car subs))
	  (subs-to-lambdas (cdr subs)))))


; (b) Determining the theory in which to conduct the proofs.
;
; When we prove the functional instantiation constraints, ideally we
; should work in an environment where the only definitions that are
; enabled are the definitions used in the functional instantiation 
; hints.  
;
; Well, the definitions we need are (almost) simply all of the
; function symbols in the right-hand side of the substitution list.
; In other words, for the above substitutions, I would want to have
; the definitions of not, in, all-not-in, and exists-not-in available.
;
; Now, the problem with this approach is, what if those symbols don't
; have definitions?  This can occur if, for example, we are using a
; constrained function in the substitution list.  This is actually
; useful, e.g., for substituting (predicate ?x) -> (not (predicate
; ?x)).  
;
; My solution is a stupid hack.  We simply pass in the names of the 
; generic functions for which we do not want to generate definitions
; along with the substitutinos.
;
; To begin, the following function will extract all function symbols
; that occur within a term.

(mutual-recursion 

  (defun term-functions (term)
    (if (atom term)
        nil
      (cons (car term) 
	    (term-list-functions (cdr term)))))

  (defun term-list-functions (list)
    (if (endp list)
	nil
      (append (term-functions (car list))
	      (term-list-functions (cdr list)))))
)

; Next, I wrote the following function, which walks over the
; substitution list and extracts the function symbols from each right
; hand side, using term-functions.  The net result is the list of all
; functions that were used in replacements.
  
(defun subs-repl-functions (subs)
  (if (endp subs)
      nil
    (let* ((sub1 (car subs))
	   (repl (second sub1)))
      (append (term-functions repl)
	      (subs-repl-functions (cdr subs))))))

; Given the above, we could then convert the list of function symbols 
; into a list of (:definition f)'s with the following function.
   
(defun function-list-to-definitions (funcs)
  (if (endp funcs)
      nil
    (cons `(:definition ,(car funcs))
	  (function-list-to-definitions (cdr funcs)))))

; And finally, here is a function that does "all of the work", calling
; function-list-to-definitions for all of the functions found in the
; substitution list, minus all of the generic functions that we don't
; want to generate :definition hints for.

(defun subs-to-defs (subs generics)
  (let* ((all-fns (subs-repl-functions subs))
	 (real-fns (set-difference-eq all-fns generics)))
    (function-list-to-definitions real-fns)))


; (c) Handling rule classes and other optional components.
;
; We are interested in several parts of a defthm.  In addition to the
; conjecture itself, we need to consider the rule-classes used by the
; theorem, and the other optional attributes such as the :hints, :doc,
; :otf-flg, etc.  We parse these attributes into a five-tuple of pairs
; of the form (present . value), where present is a boolean that says
; whether or not the flag has been seen, value is its value, and the
; order of the elements is rule-classes, instructions, hints, otf-flg,
; and finally doc.  We parse these options with the following code:

(defconst *default-parse-values* 
  '((nil . nil) (nil . nil) (nil . nil) (nil . nil) (nil . nil)))

(defun parse-defthm-option (option return-value)
  (cond ((equal (first option) :rule-classes)
         (update-nth 0 (list t (second option)) return-value))
	((equal (first option) :instructions)
	 (update-nth 1 (list t (second option)) return-value))
	((equal (first option) :hints)
	 (update-nth 2 (list t (second option)) return-value))
	((equal (first option) :otf-flg)
	 (update-nth 3 (list t (second option)) return-value))
	((equal (first option) :doc)
	 (update-nth 4 (list t (second option)) return-value))
	(t (er hard "Unknown flag in defthm options ~x0." (first option)))))

(defun parse-defthm-options (options return-value)
  (if (endp options)
      return-value
    (parse-defthm-options (cddr options)
			  (parse-defthm-option options return-value))))


; (d) Generating the actual defthm event.
;
; When we are ready to instance a defthm event, we combine the above
; work with a few new things.  First of all, we need the original
; theorem event, a new name to use, the substitutions to use, and the
; list of generic function symbols in use so that we do not create
; (:definition f) entries for them.
;
; We begin by making our substitutions in the body of the theorem.  We
; then parse the optional components of the defthm, but we only are
; interested in the rule-classes.  (Hints, instructions, and otf-flg
; will not be needed, because we will be proving this via functional
; instantiation.  Doc we ignore for no good reason.)  We construct a
; new theorem that has our new name and body, replicating the rule
; classes if necessary.  We also provide a functional instantiation
; hint of the generic theorem's name, along with a list of lambda
; substitutions to make.

(defun instance-defthm (event new-name subs generics extra-defs)
  (let* ((defthm-symbol (first event)) 
	 (defthm-name   (second event))
	 (defthm-body   (third event))
	 (new-body      (instance-rewrite defthm-body subs))
	 (options       (parse-defthm-options (cdddr event) 
					      *default-parse-values*))
	 (rc-opt        (first options)))
    `(,defthm-symbol ,new-name 
       ,new-body
       :hints(("Goal"
	       :use (:functional-instance ,defthm-name
					  ,@(subs-to-lambdas subs))
	       :in-theory (union-theories (theory 'minimal-theory)
			   (union-theories ',extra-defs
					   ',(subs-to-defs subs generics)))))
       ,@(if (car rc-opt) `(:rule-classes ,(cdr rc-opt)) nil))))



; Instantiating Encapsulates
;
;
; There are two reasons that I typically use encapsulation.  The first
; is as a purely structural/organizational purpose, where I am trying
; to prove some theorem is true, but I need some lemmas to do so.  In
; this case I use an (encapsulate nil ...) and wrap my lemmas in local
; forms.  The other reason is to actually go ahead and introduce
; constrained functions.
;
; Two strategies will be necessary for handling these situations.  In
; particular, if we are in an encapsulate which has no constrained
; function symbols, we will want to skip all local events and only add
; the non-local events (using functional instantiation to create the
; theorems).  On the other hand, for the case when we are introducing
; constrained functions, we will want to introduce new constrained 
; functions based on the encapsulate.
;
; So, encapsulates are handled separately based on whether or not any
; functions are constrained.
;
; Within an (encapsulate nil ...), local events will be skipped and
; defthm events will be proven using the functional instantiation of
; their generic counterparts.
;
; Within an (encapsulate (...) ...), local events will not be skipped
; but will instead be reintroduced with new names.  Further, defthm
; events will be copied using new names and will not be proven using
; functional instantiation.
;
; The only "extra" thing we really need for handling encapsulates is a
; system to make the substitutions within the signatures.  We do that
; here by simple rewriting.  Note that we do not allow the number of
; return values to change.  I don't really think of this as a major
; limitation, since almost always my constrained functions return a 
; single value.  If you have an example of where this would be useful,
; it would be interesting to see it.

(defun instance-signature (signature subs)
  (let ((name (first signature))
	(rest (rest signature)))
    (cons (instance-rewrite subs name) rest)))

(defun instance-signatures (signatures subs)
  (if (endp signatures)
      nil
    (cons (instance-signature (car signatures) subs)
	  (instance-signatures (cdr signatures) subs))))

; Because encapsulates can contain many events within them, it is
; natural to make them mutually recursive with the main event list
; handler, which we are now ready to introduce.





; Instantiating Entire Theories
;
; 
; We are now ready to introduce the functions which will walk through
; a theory and call the appropriate instancing functions on each of 
; the forms we encounter.  To support encapsulation, our functions 
; here are all mutually recursive.
;
; The arguments that we pass around are the following:
;
;   - The event or event list to instantiate 
;
;   - The global list of substitutions used to derive the instance
;
;   - A suffix which will be appended to generate new names
;
;   - A list of generic functions which have no definitions
;
;   - A mode, which is either 'constrained to indicate that the
;     nearest encapsulate event has constrained functions, or is nil
;     to indicate that the nearest encapsulate is merely a structural
;     wrapper for local lemmas.
;
; Finally, we overload our behavior based on suffix, so that if no 
; suffix is given, we simply replicate the generic theory instead 
; of instantiating a concrete instance of it.


(mutual-recursion

  (defun instance-event (event subs suffix generics mode extra-defs)
    (if (null suffix)
	event
      (cond ((or (eq (car event) 'defun)
		 (eq (car event) 'defund))
	     (instance-defun event subs))
	    ((or (eq (car event) 'defthm)
		 (eq (car event) 'defthmd))
	     (let* ((name (second event))
		    (new-name (intern-in-package-of-symbol
			       (string-upcase 
				(concatenate 'string 
					     (symbol-name name)
					     (symbol-name suffix)))
			       suffix)))
	       (instance-defthm event new-name subs generics extra-defs)))
	    ((equal (car event) 'local)
	     (if (eq mode 'constrained)
		 (instance-event (second event) subs suffix generics mode extra-defs)
	       nil))
	    ((equal (car event) 'encapsulate)
	     (instance-encapsulate event subs suffix generics mode extra-defs))
	    (t (er hard "Don't know how to handle ~x0" (car event))))))

  (defun instance-event-list (events subs suffix generics mode extra-defs)
    (if (endp events)
	nil
      (let ((first (instance-event (car events) subs suffix generics mode extra-defs))
	    (rest  (instance-event-list (cdr events) subs suffix generics mode extra-defs)))
	(if first 
	    (cons first rest)
	  rest))))

  (defun instance-encapsulate (event subs suffix generics mode extra-defs)
    (declare (ignore mode))
    (let* ((signatures (second event))
	   (new-sigs   (if signatures 
			   (instance-signatures subs signatures)
			 nil))
	   (new-events (instance-event-list (cddr event) subs suffix generics
					    (if signatures 
						'constrained
					      nil)
					    extra-defs)))
      `(encapsulate ,new-sigs ,@new-events)))

)


; To be able to actually introduce the events, we need to emit a macro that
; can be used to actually perform substitutions.

(defmacro instance (theory)
  (let ((macro-name (intern-in-package-of-symbol
		     (string-upcase (concatenate 'string 
						 "instance-" (string theory)))
		     theory)))
    `(defmacro ,macro-name (&key subs suffix generics extra-defs)
       (list* 'encapsulate 
	      nil
	      (instance-event-list ,theory subs suffix generics nil extra-defs)))))




; Some thoughts
;
; A fundamental issue seems to be that a function and its arguments
; are not always used in a consistent manner.  For example, say we
; want to rewrite (all ?x) to (all-foo ?x y) and we want to rewrite
; (predicate ?x) to (not (foo ?x y)).  How can we accurately say just
; what it is that we want to rewrite in each case?
;
; Right now our substitutions are based on 
;  ( (predicate ?x)  (not (foo ?x y)) )
;  ( (all ?x)        (all-foo ?x y) )
;
; We can easily pick out and say "all" is replaced by "all-foo", 
; but if we try to just use the car of the term as its symbol
; replacement, then "predicate" would be "not".  
;
; OK, so we could do some kind of preprocessing step where we fill
; in argument guards.  The "generics" list right now is a big huge
; hack that allows us to ignore the fact that :predicate doens't
; have a definition.  Really the issue that this is trying to solve
; is to tell us how to build our :in-theory event.  Right now the 
; :in-theory event is just a hack that we don't really understand.
